From 97a7e3096abab6b8dcdfe76afe52384f952ee6d0 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 21 Sep 2022 17:57:14 +0300 Subject: [PATCH 01/63] Basics --- Cargo.lock | 5 +- frame/nfts/Cargo.toml | 1 + frame/nfts/src/functions.rs | 58 +++++ frame/nfts/src/impl_nonfungibles.rs | 1 + frame/nfts/src/lib.rs | 48 ++++ frame/nfts/src/mock.rs | 7 +- frame/nfts/src/tests.rs | 378 ++++++++++++++++++++++++++-- frame/nfts/src/types.rs | 124 ++++++++- 8 files changed, 591 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d56a05fc0f7ed..cef247d87bafb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1881,9 +1881,9 @@ dependencies = [ [[package]] name = "enumflags2" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3ab37dc79652c9d85f1f7b6070d77d321d2467f5fe7b00d6b7a86c57b092ae" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" dependencies = [ "enumflags2_derive", ] @@ -5911,6 +5911,7 @@ dependencies = [ name = "pallet-nfts" version = "4.0.0-dev" dependencies = [ + "enumflags2", "frame-benchmarking", "frame-support", "frame-system", diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index 7f1ce4ff416b0..20d6c4b7f083f 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -14,6 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +enumflags2 = { version = "0.7.5" } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 27ab752dbabf6..d6aa5bd780a77 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -18,6 +18,7 @@ //! Various pieces of common functionality. use super::*; +use enumflags2::BitFlags; use frame_support::{ ensure, traits::{ExistenceRequirement, Get}, @@ -39,6 +40,14 @@ impl, I: 'static> Pallet { ensure!(!collection_details.is_frozen, Error::::Frozen); ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); + let config = + CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; + let user_features: BitFlags = config.user_features.get(); + ensure!( + !user_features.contains(UserFeature::NonTransferableItems), + Error::::ItemsNotTransferable + ); + let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; ensure!(!details.is_frozen, Error::::Frozen); @@ -69,6 +78,7 @@ impl, I: 'static> Pallet { pub fn do_create_collection( collection: T::CollectionId, owner: T::AccountId, + user_config: UserFeatures, admin: T::AccountId, deposit: DepositBalanceOf, free_holding: bool, @@ -94,6 +104,12 @@ impl, I: 'static> Pallet { }, ); + let collection_config = CollectionConfig { + system_features: SystemFeatures::new((T::DefaultSystemConfig::get()).get()), + user_features: user_config, + }; + CollectionConfigs::::insert(&collection, collection_config); + CollectionAccount::::insert(&owner, &collection, ()); Self::deposit_event(event); Ok(()) @@ -229,6 +245,14 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner == sender, Error::::NoPermission); + let config = + CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; + let user_features: BitFlags = config.user_features.get(); + ensure!( + !user_features.contains(UserFeature::NonTransferableItems), + Error::::ItemsNotTransferable + ); + if let Some(ref price) = price { ItemPriceOf::::insert(&collection, &item, (price, whitelisted_buyer.clone())); Self::deposit_event(Event::ItemPriceSet { @@ -254,6 +278,14 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner != buyer, Error::::NoPermission); + let config = + CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; + let user_features: BitFlags = config.user_features.get(); + ensure!( + !user_features.contains(UserFeature::NonTransferableItems), + Error::::NotForSale + ); + let price_info = ItemPriceOf::::get(&collection, &item).ok_or(Error::::NotForSale)?; @@ -284,4 +316,30 @@ impl, I: 'static> Pallet { Ok(()) } + + pub fn do_change_collection_config( + id: T::CollectionId, + caller: T::AccountId, + current_config: CollectionConfig, + new_config: UserFeatures, + ) -> DispatchResult { + let collection = Collection::::get(id).ok_or(Error::::UnknownCollection)?; + ensure!(collection.owner == caller, Error::::NoPermission); + + let user_features: BitFlags = current_config.user_features.get(); + + if user_features.contains(UserFeature::IsLocked) { + return Err(Error::::CollectionIsLocked.into()) + } + + CollectionConfigs::::try_mutate(id, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; + + config.user_features = new_config; + + Self::deposit_event(Event::::CollectionConfigChanged { id }); + + Ok(()) + }) + } } diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index cead6f562ab58..bff4d768b7d6c 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -95,6 +95,7 @@ impl, I: 'static> Create<::AccountId> for Pallet Self::do_create_collection( *collection, who.clone(), + UserFeatures::new(UserFeature::Administration.into()), admin.clone(), T::CollectionDeposit::get(), false, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index cdb098d2eceed..4f9d03399e901 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -42,6 +42,7 @@ mod types; pub mod weights; use codec::{Decode, Encode}; +use enumflags2::BitFlags; use frame_support::{ traits::{ tokens::Locker, BalanceStatus::Reserved, Currency, EnsureOriginWithArg, ReservableCurrency, @@ -154,6 +155,8 @@ pub mod pallet { #[pallet::constant] type ApprovalsLimit: Get; + type DefaultSystemConfig: Get; + #[cfg(feature = "runtime-benchmarks")] /// A set of helper functions for benchmarking. type Helper: BenchmarkHelper; @@ -278,6 +281,11 @@ pub mod pallet { pub(super) type CollectionMaxSupply, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::CollectionId, u32, OptionQuery>; + /// Maps a unique collection id to it's config. + #[pallet::storage] + pub(super) type CollectionConfigs, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -372,6 +380,8 @@ pub mod pallet { OwnershipAcceptanceChanged { who: T::AccountId, maybe_collection: Option }, /// Max supply has been set for a collection. CollectionMaxSupplySet { collection: T::CollectionId, max_supply: u32 }, + /// The config of a collection has change. + CollectionConfigChanged { id: T::CollectionId }, /// The price was set for the instance. ItemPriceSet { collection: T::CollectionId, @@ -407,6 +417,8 @@ pub mod pallet { BadWitness, /// The item ID is already taken. InUse, + /// Items within that collection are non-transferable. + ItemsNotTransferable, /// The item or collection is frozen. Frozen, /// The provided account is not a delegate. @@ -419,6 +431,8 @@ pub mod pallet { Unaccepted, /// The item is locked. Locked, + /// The collection is locked. + CollectionIsLocked, /// All items have been minted. MaxSupplyReached, /// The max supply has already been set. @@ -469,6 +483,7 @@ pub mod pallet { pub fn create( origin: OriginFor, collection: T::CollectionId, + config: UserFeatures, admin: AccountIdLookupOf, ) -> DispatchResult { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; @@ -477,6 +492,7 @@ pub mod pallet { Self::do_create_collection( collection, owner.clone(), + config, admin.clone(), T::CollectionDeposit::get(), false, @@ -506,6 +522,7 @@ pub mod pallet { origin: OriginFor, collection: T::CollectionId, owner: AccountIdLookupOf, + config: UserFeatures, free_holding: bool, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -514,6 +531,7 @@ pub mod pallet { Self::do_create_collection( collection, owner.clone(), + config, owner.clone(), Zero::zero(), free_holding, @@ -521,6 +539,19 @@ pub mod pallet { ) } + #[pallet::weight(0)] + pub fn change_collection_config( + origin: OriginFor, + id: T::CollectionId, + new_config: UserFeatures, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let current_config = + CollectionConfigs::::get(id).ok_or(Error::::UnknownCollection)?; + Self::do_change_collection_config(id, sender, current_config, new_config)?; + Ok(()) + } + /// Destroy a collection of fungible items. /// /// The origin must conform to `ForceOrigin` or must be `Signed` and the sender must be the @@ -954,6 +985,14 @@ pub mod pallet { let delegate = T::Lookup::lookup(delegate)?; + let config = CollectionConfigs::::get(collection) + .ok_or(Error::::UnknownCollection)?; + let user_features: BitFlags = config.user_features.get(); + ensure!( + !user_features.contains(UserFeature::NonTransferableItems), + Error::::ItemsNotTransferable + ); + let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; let mut details = @@ -1388,6 +1427,15 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; + let config = CollectionConfigs::::get(collection) + .ok_or(Error::::UnknownCollection)?; + + let user_features: BitFlags = config.user_features.get(); + ensure!( + !user_features.contains(UserFeature::IsLocked), + Error::::CollectionIsLocked + ); + let mut details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; if let Some(check_owner) = &maybe_check_owner { diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index bfa6c185ed78c..4fa4a1a381902 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -21,7 +21,7 @@ use super::*; use crate as pallet_nfts; use frame_support::{ - construct_runtime, + construct_runtime, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, }; use sp_core::H256; @@ -84,6 +84,10 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } +parameter_types! { + pub NoDeposit: SystemFeatures = SystemFeatures::new(SystemFeature::NoDeposit.into()); +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type CollectionId = u32; @@ -101,6 +105,7 @@ impl Config for Test { type KeyLimit = ConstU32<50>; type ValueLimit = ConstU32<50>; type ApprovalsLimit = ConstU32<10>; + type DefaultSystemConfig = NoDeposit; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 19d24f4924d46..d94201e96b7bb 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -22,6 +22,9 @@ use frame_support::{assert_noop, assert_ok, dispatch::Dispatchable, traits::Curr use pallet_balances::Error as BalancesError; use sp_std::prelude::*; +pub const DEFAULT_SYSTEM_FEATURES: SystemFeature = SystemFeature::NoDeposit; +pub const DEFAULT_USER_FEATURES: UserFeature = UserFeature::Administration; + fn items() -> Vec<(u64, u32, u32)> { let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); r.sort(); @@ -88,6 +91,21 @@ fn events() -> Vec> { result } +fn get_id_from_event() -> Result<::CollectionId, &'static str> { + let last_event = System::events().pop(); + if let Some(e) = last_event.clone() { + match e.event { + mock::RuntimeEvent::Nfts(inner_event) => match inner_event { + Event::ForceCreated { collection, .. } => return Ok(collection), + _ => {}, + }, + _ => {}, + } + } + + Err("bad event") +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -98,23 +116,69 @@ fn basic_setup_works() { #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(items(), vec![(1, 0, 42)]); - assert_ok!(Nfts::force_create(Origin::root(), 1, 2, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 1, + 2, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_eq!(collections(), vec![(1, 0), (2, 1)]); assert_ok!(Nfts::mint(Origin::signed(2), 1, 69, 1)); assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); }); } +#[test] +fn collection_locking_should_work() { + new_test_ext().execute_with(|| { + let user_id = 1; + + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + user_id, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); + + let id = get_id_from_event().unwrap(); + let new_config = UserFeatures::new(UserFeature::IsLocked.into()); + + assert_ok!(Nfts::change_collection_config(Origin::signed(user_id), id, new_config)); + + let collection_config = CollectionConfigs::::get(id); + + let expected_config = CollectionConfig { + system_features: SystemFeatures::new(DEFAULT_SYSTEM_FEATURES.into()), + user_features: new_config, + }; + + assert_eq!(Some(expected_config), collection_config); + }); +} + #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(Origin::signed(1), 0, 1)); + assert_ok!(Nfts::create( + Origin::signed(1), + 0, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + 1 + )); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0, 0], false)); @@ -157,7 +221,12 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(Origin::signed(1), 0, 1)); + assert_ok!(Nfts::create( + Origin::signed(1), + 0, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + 1 + )); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -168,7 +237,13 @@ fn destroy_with_bad_witness_should_not_work() { #[test] fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Nfts::owner(0, 42).unwrap(), 1); assert_eq!(collections(), vec![(1, 0)]); @@ -179,7 +254,13 @@ fn mint_should_work() { #[test] fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::transfer(Origin::signed(2), 0, 42, 3)); @@ -188,13 +269,36 @@ fn transfer_should_work() { assert_ok!(Nfts::approve_transfer(Origin::signed(3), 0, 42, 2, None)); assert_ok!(Nfts::transfer(Origin::signed(2), 0, 42, 4)); + + // validate we can't transfer non-transferable items + let collection_id = 1; + assert_ok!(Nfts::force_create( + Origin::root(), + 1, + 1, + UserFeatures::new(UserFeature::NonTransferableItems.into()), + true + )); + + assert_ok!(Nfts::mint(Origin::signed(1), 1, 1, 42)); + + assert_noop!( + Nfts::transfer(Origin::signed(1), collection_id, 42, 3,), + Error::::ItemsNotTransferable + ); }); } #[test] fn freezing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Nfts::freeze(Origin::signed(1), 0, 42)); assert_noop!(Nfts::transfer(Origin::signed(1), 0, 42, 2), Error::::Frozen); @@ -211,7 +315,13 @@ fn freezing_should_work() { #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&2, 100); @@ -236,7 +346,12 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create(Origin::signed(1), 0, 1)); + assert_ok!(Nfts::create( + Origin::signed(1), + 0, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + 1 + )); assert_eq!(collections(), vec![(1, 0)]); assert_noop!(Nfts::transfer_ownership(Origin::signed(1), 0, 2), Error::::Unaccepted); assert_ok!(Nfts::set_accept_ownership(Origin::signed(2), Some(0))); @@ -275,7 +390,13 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); assert_ok!(Nfts::mint(Origin::signed(2), 0, 42, 2)); @@ -294,7 +415,13 @@ fn set_collection_metadata_should_work() { Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), Error::::UnknownCollection, ); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); // Cannot add metadata to unowned item assert_noop!( Nfts::set_collection_metadata(Origin::signed(2), 0, bvec![0u8; 20], false), @@ -351,7 +478,13 @@ fn set_item_metadata_should_work() { Balances::make_free_balance_be(&1, 30); // Cannot add metadata to unknown item - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned item assert_noop!( @@ -396,6 +529,17 @@ fn set_item_metadata_should_work() { ); assert_ok!(Nfts::clear_metadata(Origin::signed(1), 0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 42)); + + // collection's metadata can't be changed after the collection gets locked + assert_ok!(Nfts::change_collection_config( + Origin::signed(1), + 0, + UserFeatures::new(UserFeature::IsLocked.into()) + )); + assert_noop!( + Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), + Error::::CollectionIsLocked + ); }); } @@ -404,7 +548,13 @@ fn set_attribute_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -449,7 +599,13 @@ fn set_attribute_should_respect_freeze() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -481,7 +637,13 @@ fn force_item_status_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 69, 2)); assert_ok!(Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0; 20], false)); @@ -515,7 +677,13 @@ fn force_item_status_should_work() { fn burn_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, false)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + false + )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); assert_noop!( @@ -539,7 +707,13 @@ fn burn_works() { #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::transfer(Origin::signed(3), 0, 42, 4)); @@ -548,13 +722,36 @@ fn approval_lifecycle_works() { assert_ok!(Nfts::approve_transfer(Origin::signed(4), 0, 42, 2, None)); assert_ok!(Nfts::transfer(Origin::signed(2), 0, 42, 2)); + + // ensure we can't buy an item when the collection has a NonTransferableItems flag + let collection_id = 1; + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + 1, + UserFeatures::new(UserFeature::NonTransferableItems.into()), + true + )); + + assert_ok!(Nfts::mint(Origin::signed(1), 1, collection_id, 1)); + + assert_noop!( + Nfts::approve_transfer(Origin::signed(1), collection_id, 1, 2, None), + Error::::ItemsNotTransferable + ); }); } #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -601,7 +798,13 @@ fn cancel_approval_works() { #[test] fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); let current_block = 1; @@ -620,7 +823,13 @@ fn approving_multiple_accounts_works() { #[test] fn approvals_limit_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); for i in 3..13 { @@ -640,7 +849,13 @@ fn approval_deadline_works() { System::set_block_number(0); assert!(System::block_number().is_zero()); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); // the approval expires after the 2nd block. @@ -664,7 +879,13 @@ fn approval_deadline_works() { #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -692,7 +913,13 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -714,7 +941,13 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + 0, + 1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -747,7 +980,13 @@ fn max_supply_should_work() { let max_supply = 2; // validate set_collection_max_supply - assert_ok!(Nfts::force_create(Origin::root(), collection_id, user_id, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + user_id, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert!(!CollectionMaxSupply::::contains_key(collection_id)); assert_ok!(Nfts::set_collection_max_supply( @@ -793,7 +1032,13 @@ fn set_price_should_work() { let item_1 = 1; let item_2 = 2; - assert_ok!(Nfts::force_create(Origin::root(), collection_id, user_id, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + user_id, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(user_id), collection_id, item_1, user_id)); assert_ok!(Nfts::mint(Origin::signed(user_id), collection_id, item_2, user_id)); @@ -830,6 +1075,23 @@ fn set_price_should_work() { item: item_2 })); assert!(!ItemPriceOf::::contains_key(collection_id, item_2)); + + // ensure we can't set price when the items are non-transferable + let collection_id = 1; + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + user_id, + UserFeatures::new(UserFeature::NonTransferableItems.into()), + true + )); + + assert_ok!(Nfts::mint(Origin::signed(user_id), collection_id, item_1, user_id)); + + assert_noop!( + Nfts::set_price(Origin::signed(user_id), collection_id, item_1, Some(2), None), + Error::::ItemsNotTransferable + ); }); } @@ -851,7 +1113,13 @@ fn buy_item_should_work() { Balances::make_free_balance_be(&user_2, initial_balance); Balances::make_free_balance_be(&user_3, initial_balance); - assert_ok!(Nfts::force_create(Origin::root(), collection_id, user_1, true)); + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + user_1, + UserFeatures::new(DEFAULT_USER_FEATURES.into()), + true + )); assert_ok!(Nfts::mint(Origin::signed(user_1), collection_id, item_1, user_1)); assert_ok!(Nfts::mint(Origin::signed(user_1), collection_id, item_2, user_1)); @@ -952,5 +1220,61 @@ fn buy_item_should_work() { }); assert_noop!(buy_item_call.dispatch(Origin::signed(user_2)), Error::::Frozen); } + + // ensure we can't buy an item when the collection has a NonTransferableItems flag + let collection_id = 1; + assert_ok!(Nfts::force_create( + Origin::root(), + collection_id, + user_1, + UserFeatures::new(UserFeature::NonTransferableItems.into()), + true + )); + + assert_ok!(Nfts::mint(Origin::signed(user_1), collection_id, item_1, user_1)); + + assert_noop!( + Nfts::buy_item(Origin::signed(user_2), collection_id, item_1, price_1.into()), + Error::::NotForSale + ); + }); +} + +#[test] +fn different_user_flags() { + new_test_ext().execute_with(|| { + // when setting one feature it's required to call .into() on it + let user_features = UserFeatures::new(UserFeature::IsLocked.into()); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, user_features, false)); + + let collection_config = CollectionConfigs::::get(0); + let stored_user_features = collection_config.unwrap().user_features.get(); + assert!(stored_user_features.contains(UserFeature::IsLocked)); + assert!(!stored_user_features.contains(UserFeature::Administration)); + + // no need to call .into() for multiple features + let user_features = UserFeatures::new(UserFeature::Administration | UserFeature::IsLocked); + assert_ok!(Nfts::force_create(Origin::root(), 1, 1, user_features, false)); + let collection_config = CollectionConfigs::::get(1); + let stored_user_features = collection_config.unwrap().user_features.get(); + assert!(stored_user_features.contains(UserFeature::IsLocked)); + assert!(stored_user_features.contains(UserFeature::Administration)); + + assert_ok!(Nfts::force_create( + Origin::root(), + 2, + 1, + UserFeatures::new(BitFlags::EMPTY), + false + )); + + use enumflags2::BitFlag; + assert_ok!(Nfts::force_create( + Origin::root(), + 3, + 1, + UserFeatures::new(UserFeature::empty()), + false + )); }); } diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index db1c351c4a9c5..3c0b0c3791277 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -18,11 +18,12 @@ //! Various basic types for use in the Nfts pallet. use super::*; +use enumflags2::{bitflags, BitFlags}; use frame_support::{ pallet_prelude::{BoundedVec, MaxEncodedLen}, traits::Get, }; -use scale_info::TypeInfo; +use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; pub(super) type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; @@ -127,3 +128,124 @@ pub struct ItemMetadata> { /// Whether the item metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } + +// Support for up to 64 user-enabled features on a collection. +#[bitflags] +#[repr(u64)] +#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub enum UserFeature { + Administration, + IsLocked, + NonTransferableItems, + MetadataIsLocked, + AttributesAreLocked, +} + +/// Wrapper type for `BitFlags` that implements `Codec`. +#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] +pub struct UserFeatures(pub BitFlags); + +impl UserFeatures { + pub fn new(input: BitFlags) -> Self { + UserFeatures(input) + } + + pub fn get(&self) -> BitFlags { + self.0.clone() + } +} + +impl MaxEncodedLen for UserFeatures { + fn max_encoded_len() -> usize { + u64::max_encoded_len() + } +} + +impl Encode for UserFeatures { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } +} +impl Decode for UserFeatures { + fn decode(input: &mut I) -> sp_std::result::Result { + let field = u64::decode(input)?; + Ok(Self(>::from_bits(field as u64).map_err(|_| "invalid value")?)) + } +} +impl TypeInfo for UserFeatures { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("UserFeature"))) + } +} + +// Support for up to 64 system-enabled features on a collection. +#[bitflags] +#[repr(u64)] +#[derive(Copy, Clone, RuntimeDebug, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub enum SystemFeature { + NoDeposit, + NoCollectionDeposit, + NoItemDeposit, + NoDataDeposit, + NoTrading, + NoAttributes, + NoApprovals, + NoSwaps, + NoClaims, +} + +/// Wrapper type for `BitFlags` that implements `Codec`. +#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)] +pub struct SystemFeatures(pub BitFlags); + +impl SystemFeatures { + pub fn new(input: BitFlags) -> Self { + SystemFeatures(input) + } + + pub fn get(&self) -> BitFlags { + self.0.clone() + } +} + +impl MaxEncodedLen for SystemFeatures { + fn max_encoded_len() -> usize { + u64::max_encoded_len() + } +} + +impl Eq for SystemFeatures {} +impl Encode for SystemFeatures { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } +} +impl Decode for SystemFeatures { + fn decode(input: &mut I) -> sp_std::result::Result { + let field = u64::decode(input)?; + Ok(Self(>::from_bits(field as u64).map_err(|_| "invalid value")?)) + } +} +impl TypeInfo for SystemFeatures { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("SystemFeature"))) + } +} + +// TODO: Implement Default + +#[derive(Encode, Decode, PartialEq, Debug, Clone, Copy, MaxEncodedLen, TypeInfo)] +pub struct CollectionConfig { + pub system_features: SystemFeatures, + pub user_features: UserFeatures, +} From a85608f59457b2dd0eca4351a3d269cdc95b5311 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 23 Sep 2022 19:06:59 +0300 Subject: [PATCH 02/63] WIP: change the data format --- frame/nfts/src/functions.rs | 27 ++--- frame/nfts/src/impl_nonfungibles.rs | 7 +- frame/nfts/src/lib.rs | 23 ++-- frame/nfts/src/mock.rs | 5 +- frame/nfts/src/tests.rs | 102 ++++++++-------- frame/nfts/src/types.rs | 110 ++++++------------ .../support/src/traits/tokens/nonfungibles.rs | 3 +- 7 files changed, 121 insertions(+), 156 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index d6aa5bd780a77..e649077d0c424 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -42,9 +42,9 @@ impl, I: 'static> Pallet { let config = CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.user_features.get(); + let user_features: BitFlags = config.get(); // TODO: can we omit the type? ensure!( - !user_features.contains(UserFeature::NonTransferableItems), + !user_features.contains(CollectionFeature::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -78,7 +78,7 @@ impl, I: 'static> Pallet { pub fn do_create_collection( collection: T::CollectionId, owner: T::AccountId, - user_config: UserFeatures, + collection_config: CollectionFeatures, admin: T::AccountId, deposit: DepositBalanceOf, free_holding: bool, @@ -104,10 +104,7 @@ impl, I: 'static> Pallet { }, ); - let collection_config = CollectionConfig { - system_features: SystemFeatures::new((T::DefaultSystemConfig::get()).get()), - user_features: user_config, - }; + // let config/*: BitFlags*/ = CollectionFeatures::new(collection_config); CollectionConfigs::::insert(&collection, collection_config); CollectionAccount::::insert(&owner, &collection, ()); @@ -247,9 +244,9 @@ impl, I: 'static> Pallet { let config = CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.user_features.get(); + let user_features: BitFlags = config.get(); ensure!( - !user_features.contains(UserFeature::NonTransferableItems), + !user_features.contains(CollectionFeature::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -280,9 +277,9 @@ impl, I: 'static> Pallet { let config = CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.user_features.get(); + let user_features: BitFlags = config.get(); ensure!( - !user_features.contains(UserFeature::NonTransferableItems), + !user_features.contains(CollectionFeature::NonTransferableItems), Error::::NotForSale ); @@ -317,16 +314,16 @@ impl, I: 'static> Pallet { Ok(()) } - pub fn do_change_collection_config( + /*pub fn do_change_collection_config( id: T::CollectionId, caller: T::AccountId, current_config: CollectionConfig, - new_config: UserFeatures, + new_config: CollectionFeatures, ) -> DispatchResult { let collection = Collection::::get(id).ok_or(Error::::UnknownCollection)?; ensure!(collection.owner == caller, Error::::NoPermission); - let user_features: BitFlags = current_config.user_features.get(); + let user_features: BitFlags = current_config.user_features.get(); if user_features.contains(UserFeature::IsLocked) { return Err(Error::::CollectionIsLocked.into()) @@ -341,5 +338,5 @@ impl, I: 'static> Pallet { Ok(()) }) - } + }*/ } diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index bff4d768b7d6c..4dced442839a1 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -85,17 +85,20 @@ impl, I: 'static> Inspect<::AccountId> for Palle } } -impl, I: 'static> Create<::AccountId> for Pallet { +impl, I: 'static> Create<::AccountId, BitFlags> + for Pallet +{ /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. fn create_collection( collection: &Self::CollectionId, who: &T::AccountId, admin: &T::AccountId, + config: &BitFlags, ) -> DispatchResult { Self::do_create_collection( *collection, who.clone(), - UserFeatures::new(UserFeature::Administration.into()), + CollectionFeatures::new(*config), admin.clone(), T::CollectionDeposit::get(), false, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 4f9d03399e901..2ac14d6a0ee20 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -155,7 +155,7 @@ pub mod pallet { #[pallet::constant] type ApprovalsLimit: Get; - type DefaultSystemConfig: Get; + type FeatureFlags: Get; #[cfg(feature = "runtime-benchmarks")] /// A set of helper functions for benchmarking. @@ -283,8 +283,9 @@ pub mod pallet { /// Maps a unique collection id to it's config. #[pallet::storage] + // TODO: rename to CollectionConfigOf ? pub(super) type CollectionConfigs, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig>; + StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionFeatures>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -483,7 +484,7 @@ pub mod pallet { pub fn create( origin: OriginFor, collection: T::CollectionId, - config: UserFeatures, + config: CollectionFeatures, admin: AccountIdLookupOf, ) -> DispatchResult { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; @@ -522,7 +523,7 @@ pub mod pallet { origin: OriginFor, collection: T::CollectionId, owner: AccountIdLookupOf, - config: UserFeatures, + config: CollectionFeatures, free_holding: bool, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -539,18 +540,18 @@ pub mod pallet { ) } - #[pallet::weight(0)] + /*#[pallet::weight(0)] pub fn change_collection_config( origin: OriginFor, id: T::CollectionId, - new_config: UserFeatures, + new_config: CollectionFeatures, ) -> DispatchResult { let sender = ensure_signed(origin)?; let current_config = CollectionConfigs::::get(id).ok_or(Error::::UnknownCollection)?; Self::do_change_collection_config(id, sender, current_config, new_config)?; Ok(()) - } + }*/ /// Destroy a collection of fungible items. /// @@ -987,9 +988,9 @@ pub mod pallet { let config = CollectionConfigs::::get(collection) .ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.user_features.get(); + let user_features: BitFlags = config.get(); ensure!( - !user_features.contains(UserFeature::NonTransferableItems), + !user_features.contains(CollectionFeature::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -1430,9 +1431,9 @@ pub mod pallet { let config = CollectionConfigs::::get(collection) .ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.user_features.get(); + let user_features: BitFlags = config.get(); ensure!( - !user_features.contains(UserFeature::IsLocked), + !user_features.contains(CollectionFeature::IsLocked), Error::::CollectionIsLocked ); diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 4fa4a1a381902..8581855081b4b 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -20,6 +20,7 @@ use super::*; use crate as pallet_nfts; +use enumflags2::BitFlags; use frame_support::{ construct_runtime, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, @@ -85,7 +86,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub NoDeposit: SystemFeatures = SystemFeatures::new(SystemFeature::NoDeposit.into()); + pub FeatureFlags: SystemFeatures = BitFlags::EMPTY; } impl Config for Test { @@ -105,7 +106,7 @@ impl Config for Test { type KeyLimit = ConstU32<50>; type ValueLimit = ConstU32<50>; type ApprovalsLimit = ConstU32<10>; - type DefaultSystemConfig = NoDeposit; + type FeatureFlags = FeatureFlags; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index d94201e96b7bb..c94c28cdb8d11 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -22,8 +22,7 @@ use frame_support::{assert_noop, assert_ok, dispatch::Dispatchable, traits::Curr use pallet_balances::Error as BalancesError; use sp_std::prelude::*; -pub const DEFAULT_SYSTEM_FEATURES: SystemFeature = SystemFeature::NoDeposit; -pub const DEFAULT_USER_FEATURES: UserFeature = UserFeature::Administration; +// pub const DEFAULT_COLLECTION_FEATURES: CollectionFeature = BitFlags::EMPTY; fn items() -> Vec<(u64, u32, u32)> { let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); @@ -112,7 +111,7 @@ fn basic_setup_works() { assert_eq!(items(), vec![]); }); } - +/* #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { @@ -120,7 +119,7 @@ fn basic_minting_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_eq!(collections(), vec![(1, 0)]); @@ -131,7 +130,7 @@ fn basic_minting_should_work() { Origin::root(), 1, 2, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_eq!(collections(), vec![(1, 0), (2, 1)]); @@ -140,7 +139,7 @@ fn basic_minting_should_work() { }); } -#[test] +/*#[test] fn collection_locking_should_work() { new_test_ext().execute_with(|| { let user_id = 1; @@ -149,25 +148,21 @@ fn collection_locking_should_work() { Origin::root(), 0, user_id, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); let id = get_id_from_event().unwrap(); - let new_config = UserFeatures::new(UserFeature::IsLocked.into()); + let new_config = CollectionFeatures::new(CollectionFeature::IsLocked.into()); assert_ok!(Nfts::change_collection_config(Origin::signed(user_id), id, new_config)); let collection_config = CollectionConfigs::::get(id); - let expected_config = CollectionConfig { - system_features: SystemFeatures::new(DEFAULT_SYSTEM_FEATURES.into()), - user_features: new_config, - }; - + let expected_config = CollectionConfig(new_config); assert_eq!(Some(expected_config), collection_config); }); -} +}*/ #[test] fn lifecycle_should_work() { @@ -176,7 +171,7 @@ fn lifecycle_should_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), 1 )); assert_eq!(Balances::reserved_balance(&1), 2); @@ -224,7 +219,7 @@ fn destroy_with_bad_witness_should_not_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), 1 )); @@ -241,7 +236,7 @@ fn mint_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -258,7 +253,7 @@ fn transfer_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -276,7 +271,7 @@ fn transfer_should_work() { Origin::root(), 1, 1, - UserFeatures::new(UserFeature::NonTransferableItems.into()), + CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), true )); @@ -296,7 +291,7 @@ fn freezing_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -319,7 +314,7 @@ fn origin_guards_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -349,7 +344,7 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), 1 )); assert_eq!(collections(), vec![(1, 0)]); @@ -394,7 +389,7 @@ fn set_team_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); @@ -419,7 +414,7 @@ fn set_collection_metadata_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); // Cannot add metadata to unowned item @@ -482,7 +477,7 @@ fn set_item_metadata_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -534,7 +529,7 @@ fn set_item_metadata_should_work() { assert_ok!(Nfts::change_collection_config( Origin::signed(1), 0, - UserFeatures::new(UserFeature::IsLocked.into()) + CollectionFeatures::new(CollectionFeature::IsLocked.into()) )); assert_noop!( Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), @@ -552,7 +547,7 @@ fn set_attribute_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); @@ -603,7 +598,7 @@ fn set_attribute_should_respect_freeze() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); @@ -641,7 +636,7 @@ fn force_item_status_should_work() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -681,7 +676,7 @@ fn burn_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), false )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); @@ -711,7 +706,7 @@ fn approval_lifecycle_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -729,7 +724,7 @@ fn approval_lifecycle_works() { Origin::root(), collection_id, 1, - UserFeatures::new(UserFeature::NonTransferableItems.into()), + CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), true )); @@ -749,7 +744,7 @@ fn cancel_approval_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -802,7 +797,7 @@ fn approving_multiple_accounts_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -827,7 +822,7 @@ fn approvals_limit_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -853,7 +848,7 @@ fn approval_deadline_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -883,7 +878,7 @@ fn cancel_approval_works_with_admin() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -917,7 +912,7 @@ fn cancel_approval_works_with_force() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -945,7 +940,7 @@ fn clear_all_transfer_approvals_works() { Origin::root(), 0, 1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -984,7 +979,7 @@ fn max_supply_should_work() { Origin::root(), collection_id, user_id, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); assert!(!CollectionMaxSupply::::contains_key(collection_id)); @@ -1036,7 +1031,7 @@ fn set_price_should_work() { Origin::root(), collection_id, user_id, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); @@ -1082,7 +1077,7 @@ fn set_price_should_work() { Origin::root(), collection_id, user_id, - UserFeatures::new(UserFeature::NonTransferableItems.into()), + CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), true )); @@ -1117,7 +1112,7 @@ fn buy_item_should_work() { Origin::root(), collection_id, user_1, - UserFeatures::new(DEFAULT_USER_FEATURES.into()), + CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), true )); @@ -1227,7 +1222,7 @@ fn buy_item_should_work() { Origin::root(), collection_id, user_1, - UserFeatures::new(UserFeature::NonTransferableItems.into()), + CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), true )); @@ -1244,27 +1239,29 @@ fn buy_item_should_work() { fn different_user_flags() { new_test_ext().execute_with(|| { // when setting one feature it's required to call .into() on it - let user_features = UserFeatures::new(UserFeature::IsLocked.into()); + let user_features = CollectionFeatures::new(CollectionFeature::IsLocked.into()); assert_ok!(Nfts::force_create(Origin::root(), 0, 1, user_features, false)); let collection_config = CollectionConfigs::::get(0); let stored_user_features = collection_config.unwrap().user_features.get(); - assert!(stored_user_features.contains(UserFeature::IsLocked)); - assert!(!stored_user_features.contains(UserFeature::Administration)); + assert!(stored_user_features.contains(CollectionFeature::IsLocked)); + assert!(!stored_user_features.contains(CollectionFeature::MetadataIsLocked)); // no need to call .into() for multiple features - let user_features = UserFeatures::new(UserFeature::Administration | UserFeature::IsLocked); + let user_features = CollectionFeatures::new( + CollectionFeature::MetadataIsLocked | CollectionFeature::IsLocked, + ); assert_ok!(Nfts::force_create(Origin::root(), 1, 1, user_features, false)); let collection_config = CollectionConfigs::::get(1); let stored_user_features = collection_config.unwrap().user_features.get(); - assert!(stored_user_features.contains(UserFeature::IsLocked)); - assert!(stored_user_features.contains(UserFeature::Administration)); + assert!(stored_user_features.contains(CollectionFeature::IsLocked)); + assert!(stored_user_features.contains(CollectionFeature::MetadataIsLocked)); assert_ok!(Nfts::force_create( Origin::root(), 2, 1, - UserFeatures::new(BitFlags::EMPTY), + CollectionFeatures::new(BitFlags::EMPTY), false )); @@ -1273,8 +1270,9 @@ fn different_user_flags() { Origin::root(), 3, 1, - UserFeatures::new(UserFeature::empty()), + CollectionFeatures::new(CollectionFeature::empty()), false )); }); } +*/ diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 3c0b0c3791277..3fcdb9d8b807f 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -18,6 +18,7 @@ //! Various basic types for use in the Nfts pallet. use super::*; +use codec::EncodeLike; use enumflags2::{bitflags, BitFlags}; use frame_support::{ pallet_prelude::{BoundedVec, MaxEncodedLen}, @@ -133,119 +134,82 @@ pub struct ItemMetadata> { #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] -pub enum UserFeature { - Administration, +pub enum CollectionFeature { IsLocked, - NonTransferableItems, - MetadataIsLocked, - AttributesAreLocked, + NonTransferableItems, // LockedItems + MetadataIsLocked, // LockedMetadata + AttributesAreLocked, // LockedAttributes } -/// Wrapper type for `BitFlags` that implements `Codec`. +// TODO: rename CollectionFeatures => CollectionConfig +// TODO: do we need ::new()? +// TODO: can we create an alias for BitFlags ? + +/// Wrapper type for `BitFlags` that implements `Codec`. #[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] -pub struct UserFeatures(pub BitFlags); +pub struct CollectionFeatures(BitFlags); -impl UserFeatures { - pub fn new(input: BitFlags) -> Self { - UserFeatures(input) +impl CollectionFeatures { + pub fn new(input: BitFlags) -> Self { + CollectionFeatures(input) } - pub fn get(&self) -> BitFlags { - self.0.clone() + // TODO: what if we rename to fields()? + pub fn get(&self) -> BitFlags { + self.0.clone() // TODO: do we need that .clone()? } } -impl MaxEncodedLen for UserFeatures { +impl MaxEncodedLen for CollectionFeatures { fn max_encoded_len() -> usize { u64::max_encoded_len() } } -impl Encode for UserFeatures { +impl Encode for CollectionFeatures { fn using_encoded R>(&self, f: F) -> R { self.0.bits().using_encoded(f) } } -impl Decode for UserFeatures { +impl EncodeLike for CollectionFeatures {} +impl Decode for CollectionFeatures { fn decode(input: &mut I) -> sp_std::result::Result { let field = u64::decode(input)?; - Ok(Self(>::from_bits(field as u64).map_err(|_| "invalid value")?)) + Ok(Self( + >::from_bits(field as u64).map_err(|_| "invalid value")?, + )) } } -impl TypeInfo for UserFeatures { + +impl TypeInfo for CollectionFeatures { type Identity = Self; fn type_info() -> Type { Type::builder() .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("UserFeature"))) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("CollectionFeature"))) } } +// TODO: remove +// #[derive(Encode, Decode, PartialEq, Debug, Clone, Copy, MaxEncodedLen, TypeInfo)] +// pub struct CollectionConfig(pub CollectionFeatures); + // Support for up to 64 system-enabled features on a collection. #[bitflags] #[repr(u64)] -#[derive(Copy, Clone, RuntimeDebug, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)] +#[derive(Copy, Clone, RuntimeDebug, PartialEq)] pub enum SystemFeature { - NoDeposit, NoCollectionDeposit, NoItemDeposit, - NoDataDeposit, + NoMetadataDeposit, + NoAttributesDeposit, NoTrading, NoAttributes, NoApprovals, NoSwaps, - NoClaims, -} - -/// Wrapper type for `BitFlags` that implements `Codec`. -#[derive(Clone, Copy, PartialEq, Default, RuntimeDebug)] -pub struct SystemFeatures(pub BitFlags); - -impl SystemFeatures { - pub fn new(input: BitFlags) -> Self { - SystemFeatures(input) - } - - pub fn get(&self) -> BitFlags { - self.0.clone() - } -} - -impl MaxEncodedLen for SystemFeatures { - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Eq for SystemFeatures {} -impl Encode for SystemFeatures { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } -} -impl Decode for SystemFeatures { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self(>::from_bits(field as u64).map_err(|_| "invalid value")?)) - } -} -impl TypeInfo for SystemFeatures { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("SystemFeature"))) - } + NoPublicMints, } -// TODO: Implement Default - -#[derive(Encode, Decode, PartialEq, Debug, Clone, Copy, MaxEncodedLen, TypeInfo)] -pub struct CollectionConfig { - pub system_features: SystemFeatures, - pub user_features: UserFeatures, -} +pub(super) type SystemFeatures = BitFlags; diff --git a/frame/support/src/traits/tokens/nonfungibles.rs b/frame/support/src/traits/tokens/nonfungibles.rs index d043a87ce7c10..7e5b02d2a5052 100644 --- a/frame/support/src/traits/tokens/nonfungibles.rs +++ b/frame/support/src/traits/tokens/nonfungibles.rs @@ -122,12 +122,13 @@ pub trait InspectEnumerable: Inspect { } /// Trait for providing the ability to create collections of nonfungible items. -pub trait Create: Inspect { +pub trait Create: Inspect { /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. fn create_collection( collection: &Self::CollectionId, who: &AccountId, admin: &AccountId, + config: &CollectionFeatures, ) -> DispatchResult; } From 6db840a75da1b7fdff7321b8e52de0a38fe5fa98 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 12:13:14 +0300 Subject: [PATCH 03/63] Refactor --- frame/nfts/src/functions.rs | 35 ++--- frame/nfts/src/impl_nonfungibles.rs | 6 +- frame/nfts/src/lib.rs | 27 ++-- frame/nfts/src/tests.rs | 135 +++++++----------- frame/nfts/src/types.rs | 47 +++--- .../support/src/traits/tokens/nonfungibles.rs | 4 +- 6 files changed, 110 insertions(+), 144 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index e649077d0c424..943cd0a12566d 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -18,7 +18,6 @@ //! Various pieces of common functionality. use super::*; -use enumflags2::BitFlags; use frame_support::{ ensure, traits::{ExistenceRequirement, Get}, @@ -41,10 +40,11 @@ impl, I: 'static> Pallet { ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); let config = - CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.get(); // TODO: can we omit the type? + CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; + + let settings = config.values(); ensure!( - !user_features.contains(CollectionFeature::NonTransferableItems), + !settings.contains(CollectionSetting::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -78,7 +78,7 @@ impl, I: 'static> Pallet { pub fn do_create_collection( collection: T::CollectionId, owner: T::AccountId, - collection_config: CollectionFeatures, + config: CollectionConfig, admin: T::AccountId, deposit: DepositBalanceOf, free_holding: bool, @@ -104,8 +104,7 @@ impl, I: 'static> Pallet { }, ); - // let config/*: BitFlags*/ = CollectionFeatures::new(collection_config); - CollectionConfigs::::insert(&collection, collection_config); + CollectionConfigOf::::insert(&collection, config); CollectionAccount::::insert(&owner, &collection, ()); Self::deposit_event(event); @@ -243,10 +242,11 @@ impl, I: 'static> Pallet { ensure!(details.owner == sender, Error::::NoPermission); let config = - CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.get(); + CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; + + let settings = config.values(); ensure!( - !user_features.contains(CollectionFeature::NonTransferableItems), + !settings.contains(CollectionSetting::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -276,10 +276,11 @@ impl, I: 'static> Pallet { ensure!(details.owner != buyer, Error::::NoPermission); let config = - CollectionConfigs::::get(collection).ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.get(); + CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; + + let settings = config.values(); ensure!( - !user_features.contains(CollectionFeature::NonTransferableItems), + !settings.contains(CollectionSetting::NonTransferableItems), Error::::NotForSale ); @@ -318,18 +319,18 @@ impl, I: 'static> Pallet { id: T::CollectionId, caller: T::AccountId, current_config: CollectionConfig, - new_config: CollectionFeatures, + new_config: CollectionConfig, ) -> DispatchResult { let collection = Collection::::get(id).ok_or(Error::::UnknownCollection)?; ensure!(collection.owner == caller, Error::::NoPermission); - let user_features: BitFlags = current_config.user_features.get(); + let settings = current_config.values(); - if user_features.contains(UserFeature::IsLocked) { + if settings.contains(CollectionSetting::IsLocked) { return Err(Error::::CollectionIsLocked.into()) } - CollectionConfigs::::try_mutate(id, |maybe_config| { + CollectionConfigOf::::try_mutate(id, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; config.user_features = new_config; diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 4dced442839a1..3639032478dc1 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -85,7 +85,7 @@ impl, I: 'static> Inspect<::AccountId> for Palle } } -impl, I: 'static> Create<::AccountId, BitFlags> +impl, I: 'static> Create<::AccountId, CollectionSettings> for Pallet { /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. @@ -93,12 +93,12 @@ impl, I: 'static> Create<::AccountId, BitFlags, + config: &CollectionSettings, ) -> DispatchResult { Self::do_create_collection( *collection, who.clone(), - CollectionFeatures::new(*config), + CollectionConfig(*config), admin.clone(), T::CollectionDeposit::get(), false, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 2ac14d6a0ee20..e1c41779aa2f3 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -42,7 +42,6 @@ mod types; pub mod weights; use codec::{Decode, Encode}; -use enumflags2::BitFlags; use frame_support::{ traits::{ tokens::Locker, BalanceStatus::Reserved, Currency, EnsureOriginWithArg, ReservableCurrency, @@ -283,9 +282,8 @@ pub mod pallet { /// Maps a unique collection id to it's config. #[pallet::storage] - // TODO: rename to CollectionConfigOf ? - pub(super) type CollectionConfigs, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionFeatures>; + pub(super) type CollectionConfigOf, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -484,7 +482,7 @@ pub mod pallet { pub fn create( origin: OriginFor, collection: T::CollectionId, - config: CollectionFeatures, + config: CollectionConfig, admin: AccountIdLookupOf, ) -> DispatchResult { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; @@ -523,7 +521,7 @@ pub mod pallet { origin: OriginFor, collection: T::CollectionId, owner: AccountIdLookupOf, - config: CollectionFeatures, + config: CollectionConfig, free_holding: bool, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -544,11 +542,11 @@ pub mod pallet { pub fn change_collection_config( origin: OriginFor, id: T::CollectionId, - new_config: CollectionFeatures, + new_config: CollectionConfig, ) -> DispatchResult { let sender = ensure_signed(origin)?; let current_config = - CollectionConfigs::::get(id).ok_or(Error::::UnknownCollection)?; + CollectionConfigOf::::get(id).ok_or(Error::::UnknownCollection)?; Self::do_change_collection_config(id, sender, current_config, new_config)?; Ok(()) }*/ @@ -986,11 +984,12 @@ pub mod pallet { let delegate = T::Lookup::lookup(delegate)?; - let config = CollectionConfigs::::get(collection) + let config = CollectionConfigOf::::get(collection) .ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.get(); + + let settings = config.values(); ensure!( - !user_features.contains(CollectionFeature::NonTransferableItems), + !settings.contains(CollectionSetting::NonTransferableItems), Error::::ItemsNotTransferable ); @@ -1428,12 +1427,12 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - let config = CollectionConfigs::::get(collection) + let config = CollectionConfigOf::::get(collection) .ok_or(Error::::UnknownCollection)?; - let user_features: BitFlags = config.get(); + let settings = config.values(); ensure!( - !user_features.contains(CollectionFeature::IsLocked), + !settings.contains(CollectionSetting::IsLocked), Error::::CollectionIsLocked ); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index c94c28cdb8d11..47a1eece5c482 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -22,8 +22,6 @@ use frame_support::{assert_noop, assert_ok, dispatch::Dispatchable, traits::Curr use pallet_balances::Error as BalancesError; use sp_std::prelude::*; -// pub const DEFAULT_COLLECTION_FEATURES: CollectionFeature = BitFlags::EMPTY; - fn items() -> Vec<(u64, u32, u32)> { let mut r: Vec<_> = Account::::iter().map(|x| x.0).collect(); r.sort(); @@ -111,28 +109,16 @@ fn basic_setup_works() { assert_eq!(items(), vec![]); }); } -/* + #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(items(), vec![(1, 0, 42)]); - assert_ok!(Nfts::force_create( - Origin::root(), - 1, - 2, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 1, 2, CollectionConfig::empty(), true)); assert_eq!(collections(), vec![(1, 0), (2, 1)]); assert_ok!(Nfts::mint(Origin::signed(2), 1, 69, 1)); assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); @@ -148,7 +134,7 @@ fn collection_locking_should_work() { Origin::root(), 0, user_id, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); @@ -157,13 +143,14 @@ fn collection_locking_should_work() { assert_ok!(Nfts::change_collection_config(Origin::signed(user_id), id, new_config)); - let collection_config = CollectionConfigs::::get(id); + let collection_config = CollectionConfigOf::::get(id); let expected_config = CollectionConfig(new_config); assert_eq!(Some(expected_config), collection_config); }); }*/ +/* #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { @@ -171,7 +158,7 @@ fn lifecycle_should_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), 1 )); assert_eq!(Balances::reserved_balance(&1), 2); @@ -219,7 +206,7 @@ fn destroy_with_bad_witness_should_not_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), 1 )); @@ -236,7 +223,7 @@ fn mint_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -253,7 +240,7 @@ fn transfer_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -291,7 +278,7 @@ fn freezing_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -314,7 +301,7 @@ fn origin_guards_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -344,7 +331,7 @@ fn transfer_owner_should_work() { assert_ok!(Nfts::create( Origin::signed(1), 0, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), 1 )); assert_eq!(collections(), vec![(1, 0)]); @@ -389,7 +376,7 @@ fn set_team_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); @@ -414,7 +401,7 @@ fn set_collection_metadata_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); // Cannot add metadata to unowned item @@ -477,7 +464,7 @@ fn set_item_metadata_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -547,7 +534,7 @@ fn set_attribute_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); @@ -598,7 +585,7 @@ fn set_attribute_should_respect_freeze() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); @@ -636,7 +623,7 @@ fn force_item_status_should_work() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -676,7 +663,7 @@ fn burn_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), false )); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); @@ -706,7 +693,7 @@ fn approval_lifecycle_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -744,7 +731,7 @@ fn cancel_approval_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -797,7 +784,7 @@ fn approving_multiple_accounts_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -822,7 +809,7 @@ fn approvals_limit_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -848,7 +835,7 @@ fn approval_deadline_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -878,7 +865,7 @@ fn cancel_approval_works_with_admin() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -912,7 +899,7 @@ fn cancel_approval_works_with_force() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -940,7 +927,7 @@ fn clear_all_transfer_approvals_works() { Origin::root(), 0, 1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); @@ -979,7 +966,7 @@ fn max_supply_should_work() { Origin::root(), collection_id, user_id, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); assert!(!CollectionMaxSupply::::contains_key(collection_id)); @@ -1031,7 +1018,7 @@ fn set_price_should_work() { Origin::root(), collection_id, user_id, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); @@ -1112,7 +1099,7 @@ fn buy_item_should_work() { Origin::root(), collection_id, user_1, - CollectionFeatures::new(DEFAULT_COLLECTION_FEATURES), + CollectionConfig::empty(), true )); @@ -1234,45 +1221,29 @@ fn buy_item_should_work() { ); }); } - +*/ #[test] -fn different_user_flags() { +fn various_collection_settings() { new_test_ext().execute_with(|| { - // when setting one feature it's required to call .into() on it - let user_features = CollectionFeatures::new(CollectionFeature::IsLocked.into()); - assert_ok!(Nfts::force_create(Origin::root(), 0, 1, user_features, false)); - - let collection_config = CollectionConfigs::::get(0); - let stored_user_features = collection_config.unwrap().user_features.get(); - assert!(stored_user_features.contains(CollectionFeature::IsLocked)); - assert!(!stored_user_features.contains(CollectionFeature::MetadataIsLocked)); - - // no need to call .into() for multiple features - let user_features = CollectionFeatures::new( - CollectionFeature::MetadataIsLocked | CollectionFeature::IsLocked, - ); - assert_ok!(Nfts::force_create(Origin::root(), 1, 1, user_features, false)); - let collection_config = CollectionConfigs::::get(1); - let stored_user_features = collection_config.unwrap().user_features.get(); - assert!(stored_user_features.contains(CollectionFeature::IsLocked)); - assert!(stored_user_features.contains(CollectionFeature::MetadataIsLocked)); - - assert_ok!(Nfts::force_create( - Origin::root(), - 2, - 1, - CollectionFeatures::new(BitFlags::EMPTY), - false - )); - - use enumflags2::BitFlag; - assert_ok!(Nfts::force_create( - Origin::root(), - 3, - 1, - CollectionFeatures::new(CollectionFeature::empty()), - false - )); + // when we set only one value it's required to call .into() on it + let config = CollectionConfig(CollectionSetting::IsLocked.into()); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, config, false)); + + let config = CollectionConfigOf::::get(0).unwrap(); + let stored_settings = config.values(); + assert!(stored_settings.contains(CollectionSetting::IsLocked)); + assert!(!stored_settings.contains(CollectionSetting::MetadataIsLocked)); + + // no need to call .into() for multiple values + let settings = + CollectionConfig(CollectionSetting::MetadataIsLocked | CollectionSetting::IsLocked); + assert_ok!(Nfts::force_create(Origin::root(), 1, 1, settings, false)); + + let config = CollectionConfigOf::::get(1).unwrap(); + let stored_settings = config.values(); + assert!(stored_settings.contains(CollectionSetting::IsLocked)); + assert!(stored_settings.contains(CollectionSetting::MetadataIsLocked)); + + assert_ok!(Nfts::force_create(Origin::root(), 2, 1, CollectionConfig::empty(), false)); }); } -*/ diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 3fcdb9d8b807f..68b471cf565a4 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -134,68 +134,63 @@ pub struct ItemMetadata> { #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] -pub enum CollectionFeature { +pub enum CollectionSetting { IsLocked, NonTransferableItems, // LockedItems MetadataIsLocked, // LockedMetadata AttributesAreLocked, // LockedAttributes } -// TODO: rename CollectionFeatures => CollectionConfig -// TODO: do we need ::new()? -// TODO: can we create an alias for BitFlags ? +pub(super) type CollectionSettings = BitFlags; -/// Wrapper type for `BitFlags` that implements `Codec`. +/// Wrapper type for `CollectionSettings` that implements `Codec`. #[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] -pub struct CollectionFeatures(BitFlags); +pub struct CollectionConfig(pub CollectionSettings); -impl CollectionFeatures { - pub fn new(input: BitFlags) -> Self { - CollectionFeatures(input) +impl CollectionConfig { + pub fn new(input: CollectionSettings) -> Self { + CollectionConfig(input) } - // TODO: what if we rename to fields()? - pub fn get(&self) -> BitFlags { - self.0.clone() // TODO: do we need that .clone()? + pub fn empty() -> Self { + CollectionConfig(BitFlags::EMPTY) + } + + pub fn values(&self) -> CollectionSettings { + self.0 } } -impl MaxEncodedLen for CollectionFeatures { +impl MaxEncodedLen for CollectionConfig { fn max_encoded_len() -> usize { u64::max_encoded_len() } } -impl Encode for CollectionFeatures { +impl Encode for CollectionConfig { fn using_encoded R>(&self, f: F) -> R { self.0.bits().using_encoded(f) } } -impl EncodeLike for CollectionFeatures {} -impl Decode for CollectionFeatures { +impl EncodeLike for CollectionConfig {} +impl Decode for CollectionConfig { fn decode(input: &mut I) -> sp_std::result::Result { let field = u64::decode(input)?; - Ok(Self( - >::from_bits(field as u64).map_err(|_| "invalid value")?, - )) + Ok(Self(CollectionSettings::from_bits(field as u64).map_err(|_| "invalid value")?)) } } -impl TypeInfo for CollectionFeatures { +impl TypeInfo for CollectionConfig { type Identity = Self; fn type_info() -> Type { Type::builder() .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("CollectionFeature"))) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("CollectionSetting"))) } } -// TODO: remove -// #[derive(Encode, Decode, PartialEq, Debug, Clone, Copy, MaxEncodedLen, TypeInfo)] -// pub struct CollectionConfig(pub CollectionFeatures); - // Support for up to 64 system-enabled features on a collection. #[bitflags] #[repr(u64)] diff --git a/frame/support/src/traits/tokens/nonfungibles.rs b/frame/support/src/traits/tokens/nonfungibles.rs index 7e5b02d2a5052..edad84e7a2179 100644 --- a/frame/support/src/traits/tokens/nonfungibles.rs +++ b/frame/support/src/traits/tokens/nonfungibles.rs @@ -122,13 +122,13 @@ pub trait InspectEnumerable: Inspect { } /// Trait for providing the ability to create collections of nonfungible items. -pub trait Create: Inspect { +pub trait Create: Inspect { /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. fn create_collection( collection: &Self::CollectionId, who: &AccountId, admin: &AccountId, - config: &CollectionFeatures, + config: &CollectionConfig, ) -> DispatchResult; } From fa3a2a7c299c982804b7c89009f9a71ef2c20006 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 12:14:53 +0300 Subject: [PATCH 04/63] Remove redundant new() method --- frame/nfts/src/types.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 68b471cf565a4..54b4b83040314 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -148,10 +148,6 @@ pub(super) type CollectionSettings = BitFlags; pub struct CollectionConfig(pub CollectionSettings); impl CollectionConfig { - pub fn new(input: CollectionSettings) -> Self { - CollectionConfig(input) - } - pub fn empty() -> Self { CollectionConfig(BitFlags::EMPTY) } From 472a7535ddb3b2b24bcc7c14fa8c99342977075d Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 13:06:33 +0300 Subject: [PATCH 05/63] Rename settings --- frame/nfts/src/lib.rs | 2 +- frame/nfts/src/tests.rs | 15 ++++++++------- frame/nfts/src/types.rs | 7 +++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index e1c41779aa2f3..90d82fa004827 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1432,7 +1432,7 @@ pub mod pallet { let settings = config.values(); ensure!( - !settings.contains(CollectionSetting::IsLocked), + !settings.contains(CollectionSetting::LockedMetadata), Error::::CollectionIsLocked ); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 47a1eece5c482..cf6b72bb381ed 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1226,23 +1226,24 @@ fn buy_item_should_work() { fn various_collection_settings() { new_test_ext().execute_with(|| { // when we set only one value it's required to call .into() on it - let config = CollectionConfig(CollectionSetting::IsLocked.into()); + let config = CollectionConfig(CollectionSetting::NonTransferableItems.into()); assert_ok!(Nfts::force_create(Origin::root(), 0, 1, config, false)); let config = CollectionConfigOf::::get(0).unwrap(); let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::IsLocked)); - assert!(!stored_settings.contains(CollectionSetting::MetadataIsLocked)); + assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); + assert!(!stored_settings.contains(CollectionSetting::LockedMetadata)); // no need to call .into() for multiple values - let settings = - CollectionConfig(CollectionSetting::MetadataIsLocked | CollectionSetting::IsLocked); + let settings = CollectionConfig( + CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, + ); assert_ok!(Nfts::force_create(Origin::root(), 1, 1, settings, false)); let config = CollectionConfigOf::::get(1).unwrap(); let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::IsLocked)); - assert!(stored_settings.contains(CollectionSetting::MetadataIsLocked)); + assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); + assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); assert_ok!(Nfts::force_create(Origin::root(), 2, 1, CollectionConfig::empty(), false)); }); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 54b4b83040314..3f322c916e990 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -135,10 +135,9 @@ pub struct ItemMetadata> { #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum CollectionSetting { - IsLocked, - NonTransferableItems, // LockedItems - MetadataIsLocked, // LockedMetadata - AttributesAreLocked, // LockedAttributes + NonTransferableItems, + LockedMetadata, + LockedAttributes, } pub(super) type CollectionSettings = BitFlags; From 2611dd41cd8f2d8e4f46b06a32d3810e27a68f01 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 13:22:26 +0300 Subject: [PATCH 06/63] Enable tests --- frame/nfts/src/tests.rs | 215 +++++++--------------------------------- 1 file changed, 35 insertions(+), 180 deletions(-) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index cf6b72bb381ed..5161e2c3d2114 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -88,21 +88,6 @@ fn events() -> Vec> { result } -fn get_id_from_event() -> Result<::CollectionId, &'static str> { - let last_event = System::events().pop(); - if let Some(e) = last_event.clone() { - match e.event { - mock::RuntimeEvent::Nfts(inner_event) => match inner_event { - Event::ForceCreated { collection, .. } => return Ok(collection), - _ => {}, - }, - _ => {}, - } - } - - Err("bad event") -} - #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -129,38 +114,32 @@ fn basic_minting_should_work() { fn collection_locking_should_work() { new_test_ext().execute_with(|| { let user_id = 1; + let collection_id = 0; assert_ok!(Nfts::force_create( Origin::root(), - 0, + collection_id, user_id, CollectionConfig::empty(), true )); - let id = get_id_from_event().unwrap(); - let new_config = CollectionFeatures::new(CollectionFeature::IsLocked.into()); + let new_config = CollectionConfig(CollectionSetting::IsLocked.into()); - assert_ok!(Nfts::change_collection_config(Origin::signed(user_id), id, new_config)); + assert_ok!(Nfts::change_collection_config(Origin::signed(user_id), collection_id, new_config)); - let collection_config = CollectionConfigOf::::get(id); + let collection_config = CollectionConfigOf::::get(collection_id); let expected_config = CollectionConfig(new_config); assert_eq!(Some(expected_config), collection_config); }); }*/ -/* #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create( - Origin::signed(1), - 0, - CollectionConfig::empty(), - 1 - )); + assert_ok!(Nfts::create(Origin::signed(1), 0, CollectionConfig::empty(), 1)); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0, 0], false)); @@ -203,12 +182,7 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create( - Origin::signed(1), - 0, - CollectionConfig::empty(), - 1 - )); + assert_ok!(Nfts::create(Origin::signed(1), 0, CollectionConfig::empty(), 1)); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); @@ -219,13 +193,7 @@ fn destroy_with_bad_witness_should_not_work() { #[test] fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_eq!(Nfts::owner(0, 42).unwrap(), 1); assert_eq!(collections(), vec![(1, 0)]); @@ -236,13 +204,7 @@ fn mint_should_work() { #[test] fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::transfer(Origin::signed(2), 0, 42, 3)); @@ -258,7 +220,7 @@ fn transfer_should_work() { Origin::root(), 1, 1, - CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), + CollectionConfig(CollectionSetting::NonTransferableItems.into()), true )); @@ -274,13 +236,7 @@ fn transfer_should_work() { #[test] fn freezing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Nfts::freeze(Origin::signed(1), 0, 42)); assert_noop!(Nfts::transfer(Origin::signed(1), 0, 42, 2), Error::::Frozen); @@ -297,13 +253,7 @@ fn freezing_should_work() { #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&2, 100); @@ -328,12 +278,7 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create( - Origin::signed(1), - 0, - CollectionConfig::empty(), - 1 - )); + assert_ok!(Nfts::create(Origin::signed(1), 0, CollectionConfig::empty(), 1)); assert_eq!(collections(), vec![(1, 0)]); assert_noop!(Nfts::transfer_ownership(Origin::signed(1), 0, 2), Error::::Unaccepted); assert_ok!(Nfts::set_accept_ownership(Origin::signed(2), Some(0))); @@ -372,13 +317,7 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); assert_ok!(Nfts::mint(Origin::signed(2), 0, 42, 2)); @@ -397,13 +336,7 @@ fn set_collection_metadata_should_work() { Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), Error::::UnknownCollection, ); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); // Cannot add metadata to unowned item assert_noop!( Nfts::set_collection_metadata(Origin::signed(2), 0, bvec![0u8; 20], false), @@ -460,13 +393,7 @@ fn set_item_metadata_should_work() { Balances::make_free_balance_be(&1, 30); // Cannot add metadata to unknown item - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned item assert_noop!( @@ -513,15 +440,15 @@ fn set_item_metadata_should_work() { assert!(!ItemMetadataOf::::contains_key(0, 42)); // collection's metadata can't be changed after the collection gets locked - assert_ok!(Nfts::change_collection_config( + /*assert_ok!(Nfts::change_collection_config( Origin::signed(1), 0, - CollectionFeatures::new(CollectionFeature::IsLocked.into()) + CollectionConfig(CollectionSetting::LockedMetadata.into()) )); assert_noop!( Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0u8; 20], false), Error::::CollectionIsLocked - ); + );*/ }); } @@ -530,13 +457,7 @@ fn set_attribute_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -581,13 +502,7 @@ fn set_attribute_should_respect_freeze() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(Origin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -619,13 +534,7 @@ fn force_item_status_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 1)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 69, 2)); assert_ok!(Nfts::set_collection_metadata(Origin::signed(1), 0, bvec![0; 20], false)); @@ -659,13 +568,7 @@ fn force_item_status_should_work() { fn burn_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), false)); assert_ok!(Nfts::set_team(Origin::signed(1), 0, 2, 3, 4)); assert_noop!( @@ -689,13 +592,7 @@ fn burn_works() { #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::transfer(Origin::signed(3), 0, 42, 4)); @@ -711,7 +608,7 @@ fn approval_lifecycle_works() { Origin::root(), collection_id, 1, - CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), + CollectionConfig(CollectionSetting::NonTransferableItems.into()), true )); @@ -727,13 +624,7 @@ fn approval_lifecycle_works() { #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -780,13 +671,7 @@ fn cancel_approval_works() { #[test] fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); let current_block = 1; @@ -805,13 +690,7 @@ fn approving_multiple_accounts_works() { #[test] fn approvals_limit_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); for i in 3..13 { @@ -831,13 +710,7 @@ fn approval_deadline_works() { System::set_block_number(0); assert!(System::block_number().is_zero()); - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); // the approval expires after the 2nd block. @@ -861,13 +734,7 @@ fn approval_deadline_works() { #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -895,13 +762,7 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -923,13 +784,7 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create( - Origin::root(), - 0, - 1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(Origin::root(), 0, 1, CollectionConfig::empty(), true)); assert_ok!(Nfts::mint(Origin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(Origin::signed(2), 0, 42, 3, None)); @@ -1064,7 +919,7 @@ fn set_price_should_work() { Origin::root(), collection_id, user_id, - CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), + CollectionConfig(CollectionSetting::NonTransferableItems.into()), true )); @@ -1209,7 +1064,7 @@ fn buy_item_should_work() { Origin::root(), collection_id, user_1, - CollectionFeatures::new(CollectionFeature::NonTransferableItems.into()), + CollectionConfig(CollectionSetting::NonTransferableItems.into()), true )); @@ -1221,7 +1076,7 @@ fn buy_item_should_work() { ); }); } -*/ + #[test] fn various_collection_settings() { new_test_ext().execute_with(|| { From 46a6b7edb76cbaa47d03d4a43ad3205d439b3121 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:01:11 +0300 Subject: [PATCH 07/63] Chore --- frame/nfts/src/mock.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 7230bdb76c2f1..3e9e8ae631836 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -20,7 +20,7 @@ use super::*; use crate as pallet_nfts; -use enumflags2::BitFlags; +use enumflags2::BitFlag; use frame_support::{ construct_runtime, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, @@ -86,7 +86,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub FeatureFlags: SystemFeatures = BitFlags::EMPTY; + pub FeatureFlags: SystemFeatures = SystemFeature::empty(); } impl Config for Test { From 5ff70486793631c21b60498d6ed74dfc73c8140f Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:07:45 +0300 Subject: [PATCH 08/63] Change params order --- frame/nfts/src/functions.rs | 2 +- frame/nfts/src/impl_nonfungibles.rs | 2 +- frame/nfts/src/lib.rs | 6 +++--- frame/nfts/src/tests.rs | 9 ++++----- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 9f92971ed1cd8..8491ca08a6f67 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -78,8 +78,8 @@ impl, I: 'static> Pallet { pub fn do_create_collection( collection: T::CollectionId, owner: T::AccountId, - config: CollectionConfig, admin: T::AccountId, + config: CollectionConfig, deposit: DepositBalanceOf, free_holding: bool, event: Event, diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 3639032478dc1..a4b5d3f8d9800 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -98,8 +98,8 @@ impl, I: 'static> Create<::AccountId, Collection Self::do_create_collection( *collection, who.clone(), - CollectionConfig(*config), admin.clone(), + CollectionConfig(*config), T::CollectionDeposit::get(), false, Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 293b4ce7f1473..0bdd93c37d670 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -524,8 +524,8 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::create())] pub fn create( origin: OriginFor, - config: CollectionConfig, admin: AccountIdLookupOf, + config: CollectionConfig, ) -> DispatchResult { let collection = NextCollectionId::::get().unwrap_or(T::CollectionId::initial_value()); @@ -536,8 +536,8 @@ pub mod pallet { Self::do_create_collection( collection, owner.clone(), - config, admin.clone(), + config, T::CollectionDeposit::get(), false, Event::Created { collection, creator: owner, owner: admin }, @@ -576,8 +576,8 @@ pub mod pallet { Self::do_create_collection( collection, owner.clone(), - config, owner.clone(), + config, Zero::zero(), free_holding, Event::ForceCreated { collection, owner }, diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 324de7297ed5c..1ac04807a40d2 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -117,8 +117,7 @@ fn collection_locking_should_work() { let collection_id = 0; assert_ok!(Nfts::force_create( - Origin::root(), - collection_id, + RuntimeOrigin::root(), user_id, CollectionConfig::empty(), true @@ -139,7 +138,7 @@ fn collection_locking_should_work() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), CollectionConfig::empty(), 1)); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0], false)); @@ -182,7 +181,7 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), CollectionConfig::empty(), 1)); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); @@ -286,7 +285,7 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), CollectionConfig::empty(), 1)); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); assert_eq!(collections(), vec![(1, 0)]); assert_noop!( Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2), From f7ddfed93041e296beb941db5e9b781002617694 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:11:38 +0300 Subject: [PATCH 09/63] Delete the config on collection removal --- frame/nfts/src/functions.rs | 1 + frame/nfts/src/tests.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 8491ca08a6f67..c773ba668c8bf 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -146,6 +146,7 @@ impl, I: 'static> Pallet { CollectionAccount::::remove(&collection_details.owner, &collection); T::Currency::unreserve(&collection_details.owner, collection_details.total_deposit); CollectionMaxSupply::::remove(&collection); + CollectionConfigOf::::remove(&collection); Self::deposit_event(Event::Destroyed { collection }); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 1ac04807a40d2..8db9784dea61e 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -167,6 +167,7 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&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)); From d0d1d60867d4ced78c47a50c4e07f977b5adf366 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:22:03 +0300 Subject: [PATCH 10/63] Chore --- frame/nfts/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 8db9784dea61e..7d26b5a130cd6 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1189,10 +1189,10 @@ fn various_collection_settings() { assert!(!stored_settings.contains(CollectionSetting::LockedMetadata)); // no need to call .into() for multiple values - let settings = CollectionConfig( + let config = CollectionConfig( CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, settings, false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); let config = CollectionConfigOf::::get(1).unwrap(); let stored_settings = config.values(); From 7d4b31f0cb4252c0ecd4423c014b3d63b6a56f50 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:22:28 +0300 Subject: [PATCH 11/63] Remove redundant system features --- frame/nfts/src/types.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 8e7089ffa6091..6e9f2ed98b848 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -210,10 +210,6 @@ impl TypeInfo for CollectionConfig { #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq)] pub enum SystemFeature { - NoCollectionDeposit, - NoItemDeposit, - NoMetadataDeposit, - NoAttributesDeposit, NoTrading, NoAttributes, NoApprovals, From dd846370a3cd65ca916c311c6e5df8f73f35e6ef Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 24 Sep 2022 14:35:48 +0300 Subject: [PATCH 12/63] Rename force_item_status to force_collection_status --- frame/nfts/src/benchmarking.rs | 6 ++-- frame/nfts/src/lib.rs | 53 +++++++++++++++++----------------- frame/nfts/src/tests.rs | 13 +++++++-- frame/nfts/src/weights.rs | 6 ++-- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 00527abc99e02..f3fd64a5457a0 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -204,7 +204,7 @@ benchmarks_instance_pallet! { let i in 0 .. 5_000; let (collection, caller, caller_lookup) = create_collection::(); let items = (0..i).map(|x| mint_item::(x as u16).0).collect::>(); - Nfts::::force_item_status( + Nfts::::force_collection_status( SystemOrigin::Root.into(), collection, caller_lookup.clone(), @@ -283,10 +283,10 @@ benchmarks_instance_pallet! { }.into()); } - force_item_status { + force_collection_status { let (collection, caller, caller_lookup) = create_collection::(); let origin = T::ForceOrigin::successful_origin(); - let call = Call::::force_item_status { + let call = Call::::force_collection_status { collection, owner: caller_lookup.clone(), issuer: caller_lookup.clone(), diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 0bdd93c37d670..3ca0814efc4c0 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -376,7 +376,7 @@ pub mod pallet { /// All approvals of an item got cancelled. AllApprovalsCancelled { collection: T::CollectionId, item: T::ItemId, owner: T::AccountId }, /// A `collection` has had its attributes changed by the `Force` origin. - ItemStatusChanged { collection: T::CollectionId }, + CollectionStatusChanged { collection: T::CollectionId }, /// New metadata has been set for a `collection`. CollectionMetadataSet { collection: T::CollectionId, @@ -1175,26 +1175,26 @@ pub mod pallet { Ok(()) } - /// Alter the attributes of a given item. + /// Alter the attributes of a given collection. /// /// Origin must be `ForceOrigin`. /// - /// - `collection`: The identifier of the item. - /// - `owner`: The new Owner of this item. - /// - `issuer`: The new Issuer of this item. - /// - `admin`: The new Admin of this item. - /// - `freezer`: The new Freezer of this item. - /// - `free_holding`: Whether a deposit is taken for holding an item of this collection. + /// - `collection`: The identifier of the collection. + /// - `owner`: The new Owner of this collection. + /// - `issuer`: The new Issuer of this collection. + /// - `admin`: The new Admin of this collection. + /// - `freezer`: The new Freezer of this collection. + /// - `free_holding`: Whether a deposit is taken for holding an item in this collection. /// - `is_frozen`: Whether this collection is frozen except for permissioned/admin /// instructions. /// - /// Emits `ItemStatusChanged` with the identity of the item. + /// Emits `CollectionStatusChanged` with the identity of the item. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::force_item_status())] - pub fn force_item_status( + #[pallet::weight(T::WeightInfo::force_collection_status())] + pub fn force_collection_status( origin: OriginFor, - collection: T::CollectionId, + collection_id: T::CollectionId, owner: AccountIdLookupOf, issuer: AccountIdLookupOf, admin: AccountIdLookupOf, @@ -1204,21 +1204,22 @@ pub mod pallet { ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - Collection::::try_mutate(collection, |maybe_item| { - let mut item = maybe_item.take().ok_or(Error::::UnknownCollection)?; - let old_owner = item.owner; + Collection::::try_mutate(collection_id, |maybe_collection| { + let mut collection = + maybe_collection.take().ok_or(Error::::UnknownCollection)?; + let old_owner = collection.owner; let new_owner = T::Lookup::lookup(owner)?; - item.owner = new_owner.clone(); - item.issuer = T::Lookup::lookup(issuer)?; - item.admin = T::Lookup::lookup(admin)?; - item.freezer = T::Lookup::lookup(freezer)?; - item.free_holding = free_holding; - item.is_frozen = is_frozen; - *maybe_item = Some(item); - CollectionAccount::::remove(&old_owner, &collection); - CollectionAccount::::insert(&new_owner, &collection, ()); - - Self::deposit_event(Event::ItemStatusChanged { collection }); + collection.owner = new_owner.clone(); + collection.issuer = T::Lookup::lookup(issuer)?; + collection.admin = T::Lookup::lookup(admin)?; + collection.freezer = T::Lookup::lookup(freezer)?; + collection.free_holding = free_holding; + collection.is_frozen = is_frozen; + *maybe_collection = Some(collection); + CollectionAccount::::remove(&old_owner, &collection_id); + CollectionAccount::::insert(&new_owner, &collection_id, ()); + + Self::deposit_event(Event::CollectionStatusChanged { collection: collection_id }); Ok(()) }) } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 7d26b5a130cd6..aa4c1eb91568e 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -578,7 +578,7 @@ fn set_attribute_should_respect_freeze() { } #[test] -fn force_item_status_should_work() { +fn force_collection_status_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); @@ -591,7 +591,16 @@ fn force_item_status_should_work() { assert_eq!(Balances::reserved_balance(1), 65); // force item status to be free holding - assert_ok!(Nfts::force_item_status(RuntimeOrigin::root(), 0, 1, 1, 1, 1, true, false)); + assert_ok!(Nfts::force_collection_status( + RuntimeOrigin::root(), + 0, + 1, + 1, + 1, + 1, + true, + false, + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 142, bvec![0; 20], false)); diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 1dab0838f32b2..39dd977674952 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -59,7 +59,7 @@ pub trait WeightInfo { fn thaw_collection() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; - fn force_item_status() -> Weight; + fn force_collection_status() -> Weight; fn set_attribute() -> Weight; fn clear_attribute() -> Weight; fn set_metadata() -> Weight; @@ -200,7 +200,7 @@ impl WeightInfo for SubstrateWeight { } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) - fn force_item_status() -> Weight { + fn force_collection_status() -> Weight { Weight::from_ref_time(25_684_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) @@ -430,7 +430,7 @@ impl WeightInfo for () { } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) - fn force_item_status() -> Weight { + fn force_collection_status() -> Weight { Weight::from_ref_time(25_684_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) From 4920a0c7e139ad1b767ed4a726d6181337d30aaa Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 10:53:02 +0300 Subject: [PATCH 13/63] Update node runtime --- bin/node/runtime/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index eb6941e85215c..4c5a4d748ae3c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -53,6 +53,7 @@ use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; +use pallet_nfts::{SystemFeature, SystemFeatures}; use pallet_session::historical::{self as pallet_session_historical}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; @@ -1496,6 +1497,10 @@ impl pallet_uniques::Config for Runtime { type Locker = (); } +parameter_types! { + pub FeatureFlags: SystemFeatures = SystemFeature::empty(); +} + impl pallet_nfts::Config for Runtime { type RuntimeEvent = RuntimeEvent; type CollectionId = u32; @@ -1512,6 +1517,7 @@ impl pallet_nfts::Config for Runtime { type ValueLimit = ValueLimit; type ApprovalsLimit = ApprovalsLimit; type MaxTips = MaxTips; + type FeatureFlags = FeatureFlags; type WeightInfo = pallet_nfts::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] type Helper = (); From 7456e73319906aacc86c4284fea0dfa271468306 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 10:57:16 +0300 Subject: [PATCH 14/63] Chore --- frame/nfts/src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 6e9f2ed98b848..fad6b505d940f 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -167,7 +167,7 @@ pub struct CollectionConfig(pub CollectionSettings); impl CollectionConfig { pub fn empty() -> Self { - CollectionConfig(BitFlags::EMPTY) + Self(BitFlags::EMPTY) } pub fn values(&self) -> CollectionSettings { @@ -217,4 +217,4 @@ pub enum SystemFeature { NoPublicMints, } -pub(super) type SystemFeatures = BitFlags; +pub type SystemFeatures = BitFlags; From 9ff45b8836fcc3f73661719f5d3ae0f498ab7dba Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 10:59:03 +0300 Subject: [PATCH 15/63] Remove thaw_collection --- frame/nfts/src/benchmarking.rs | 11 +---------- frame/nfts/src/lib.rs | 29 ----------------------------- frame/nfts/src/tests.rs | 15 +++++++++++---- frame/nfts/src/weights.rs | 13 ------------- 4 files changed, 12 insertions(+), 56 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index f3fd64a5457a0..0a60af3de1230 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -212,7 +212,7 @@ benchmarks_instance_pallet! { caller_lookup.clone(), caller_lookup, true, - false, + CollectionConfig::empty(), )?; }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone()) verify { @@ -247,15 +247,6 @@ benchmarks_instance_pallet! { assert_last_event::(Event::CollectionFrozen { collection }.into()); } - thaw_collection { - let (collection, caller, caller_lookup) = create_collection::(); - let origin = SystemOrigin::Signed(caller.clone()).into(); - Nfts::::freeze_collection(origin, collection)?; - }: _(SystemOrigin::Signed(caller.clone()), collection) - verify { - assert_last_event::(Event::CollectionThawed { collection }.into()); - } - transfer_ownership { let (collection, caller, _) = create_collection::(); let target: T::AccountId = account("target", 0, SEED); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 3ca0814efc4c0..1947cbc3fa0a1 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -345,8 +345,6 @@ pub mod pallet { Thawed { collection: T::CollectionId, item: T::ItemId }, /// Some `collection` was frozen. CollectionFrozen { collection: T::CollectionId }, - /// Some `collection` was thawed. - CollectionThawed { collection: T::CollectionId }, /// The owner changed. OwnerChanged { collection: T::CollectionId, new_owner: T::AccountId }, /// The management team changed. @@ -891,33 +889,6 @@ pub mod pallet { }) } - /// Re-allow unprivileged transfers for a whole collection. - /// - /// Origin must be Signed and the sender should be the Admin of the `collection`. - /// - /// - `collection`: The collection to be thawed. - /// - /// Emits `CollectionThawed`. - /// - /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::thaw_collection())] - pub fn thaw_collection( - origin: OriginFor, - collection: T::CollectionId, - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - - Collection::::try_mutate(collection, |maybe_details| { - let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; - ensure!(origin == details.admin, Error::::NoPermission); - - details.is_frozen = false; - - Self::deposit_event(Event::::CollectionThawed { collection }); - Ok(()) - }) - } - /// Change the Owner of a collection. /// /// Origin must be Signed and the sender should be the Owner of the `collection`. diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index aa4c1eb91568e..66893b300405a 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -247,7 +247,16 @@ fn freezing_should_work() { assert_ok!(Nfts::freeze_collection(RuntimeOrigin::signed(1), 0)); assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); - assert_ok!(Nfts::thaw_collection(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nfts::force_collection_status( + RuntimeOrigin::root(), + 0, + 1, + 1, + 1, + 1, + false, + CollectionConfig::empty(), + )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2)); }); } @@ -599,7 +608,7 @@ fn force_collection_status_should_work() { 1, 1, true, - false, + CollectionConfig::empty(), )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2)); @@ -1151,8 +1160,6 @@ fn buy_item_should_work() { Error::::Frozen ); - assert_ok!(Nfts::thaw_collection(RuntimeOrigin::signed(user_1), collection_id)); - // freeze item assert_ok!(Nfts::freeze(RuntimeOrigin::signed(user_1), collection_id, item_3)); diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 39dd977674952..b92f1fce0deb0 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -56,7 +56,6 @@ pub trait WeightInfo { fn freeze() -> Weight; fn thaw() -> Weight; fn freeze_collection() -> Weight; - fn thaw_collection() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn force_collection_status() -> Weight; @@ -178,12 +177,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: Nfts Class (r:1 w:1) - fn thaw_collection() -> Weight { - Weight::from_ref_time(22_584_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } // Storage: Nfts OwnershipAcceptance (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:2) @@ -408,12 +401,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: Nfts Class (r:1 w:1) - fn thaw_collection() -> Weight { - Weight::from_ref_time(22_584_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } // Storage: Nfts OwnershipAcceptance (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:2) From c41a48c1f17747ecfedea112702b42009a44840f Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 10:59:23 +0300 Subject: [PATCH 16/63] Chore --- frame/nfts/src/functions.rs | 2 +- frame/nfts/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index c773ba668c8bf..6044b1f91bef6 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -84,7 +84,7 @@ impl, I: 'static> Pallet { free_holding: bool, event: Event, ) -> DispatchResult { - ensure!(!Collection::::contains_key(collection), Error::::InUse); + ensure!(!Collection::::contains_key(collection), Error::::CollectionIdInUse); T::Currency::reserve(&owner, deposit)?; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 1947cbc3fa0a1..a870541b2a111 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -456,8 +456,8 @@ pub mod pallet { WrongOwner, /// Invalid witness data given. BadWitness, - /// The item ID is already taken. - InUse, + /// Collection ID is already taken. + CollectionIdInUse, /// Items within that collection are non-transferable. ItemsNotTransferable, /// The item or collection is frozen. @@ -733,7 +733,7 @@ pub mod pallet { }) } - /// Reevaluate the deposits on some items. + /// Re-evaluate the deposits on some items. /// /// Origin must be Signed and the sender should be the Owner of the `collection`. /// From fee3ade53136430d6cc06c64ed61f747c5d8d681 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 11:33:21 +0300 Subject: [PATCH 17/63] Connect collection.is_frozen to config --- frame/nfts/src/benchmarking.rs | 2 +- frame/nfts/src/features/common.rs | 32 ++++++++++++++++++++++ frame/nfts/src/features/mod.rs | 1 + frame/nfts/src/functions.rs | 37 +++++++------------------- frame/nfts/src/impl_nonfungibles.rs | 7 +++-- frame/nfts/src/lib.rs | 38 +++++++++++++------------- frame/nfts/src/tests.rs | 41 ++++++++++++++--------------- frame/nfts/src/types.rs | 2 -- 8 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 frame/nfts/src/features/common.rs diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 0a60af3de1230..26807a24cbc66 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -284,7 +284,7 @@ benchmarks_instance_pallet! { admin: caller_lookup.clone(), freezer: caller_lookup, free_holding: true, - is_frozen: false, + config: CollectionConfig::empty(), }; }: { call.dispatch_bypass_filter(origin)? } verify { diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/common.rs new file mode 100644 index 0000000000000..48db43210e097 --- /dev/null +++ b/frame/nfts/src/features/common.rs @@ -0,0 +1,32 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::pallet_prelude::*; + +impl, I: 'static> Pallet { + pub fn is_collection_setting_disabled( + collection_id: &T::CollectionId, + setting: CollectionSetting, + ) -> Result { + let config = CollectionConfigOf::::get(&collection_id) + .ok_or(Error::::UnknownCollection)?; + + let settings = config.values(); + Ok(!settings.contains(setting)) + } +} diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index 5661797978439..f4a455d772c94 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -16,3 +16,4 @@ // limitations under the License. pub mod buy_sell; +pub mod common; diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 6044b1f91bef6..837e7ddaa07e2 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -36,17 +36,13 @@ impl, I: 'static> Pallet { ) -> DispatchResult { let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(!collection_details.is_frozen, Error::::Frozen); ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); - let config = - CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; - - let settings = config.values(); - ensure!( - !settings.contains(CollectionSetting::NonTransferableItems), - Error::::ItemsNotTransferable - ); + let action_allowed = Self::is_collection_setting_disabled( + &collection, + CollectionSetting::NonTransferableItems, + )?; + ensure!(action_allowed, Error::::ItemsNotTransferable); let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; @@ -100,7 +96,6 @@ impl, I: 'static> Pallet { items: 0, item_metadatas: 0, attributes: 0, - is_frozen: false, }, ); @@ -246,14 +241,11 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner == sender, Error::::NoPermission); - let config = - CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; - - let settings = config.values(); - ensure!( - !settings.contains(CollectionSetting::NonTransferableItems), - Error::::ItemsNotTransferable - ); + let action_allowed = Self::is_collection_setting_disabled( + &collection, + CollectionSetting::NonTransferableItems, + )?; + ensure!(action_allowed, Error::::ItemsNotTransferable); if let Some(ref price) = price { ItemPriceOf::::insert(&collection, &item, (price, whitelisted_buyer.clone())); @@ -280,15 +272,6 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner != buyer, Error::::NoPermission); - let config = - CollectionConfigOf::::get(collection).ok_or(Error::::UnknownCollection)?; - - let settings = config.values(); - ensure!( - !settings.contains(CollectionSetting::NonTransferableItems), - Error::::NotForSale - ); - let price_info = ItemPriceOf::::get(&collection, &item).ok_or(Error::::NotForSale)?; diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index a4b5d3f8d9800..6b0d5a90fccac 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -78,8 +78,11 @@ impl, I: 'static> Inspect<::AccountId> for Palle /// /// Default implementation is that all items are transferable. fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool { - match (Collection::::get(collection), Item::::get(collection, item)) { - (Some(cd), Some(id)) if !cd.is_frozen && !id.is_frozen => true, + match (CollectionConfigOf::::get(collection), Item::::get(collection, item)) { + (Some(cc), Some(id)) + if !cc.values().contains(CollectionSetting::NonTransferableItems) && + !id.is_frozen => + true, _ => false, } } diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index a870541b2a111..af413fa439e0e 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -878,11 +878,15 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; - Collection::::try_mutate(collection, |maybe_details| { - let details = maybe_details.as_mut().ok_or(Error::::UnknownCollection)?; - ensure!(origin == details.freezer, Error::::NoPermission); + let details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(origin == details.freezer, Error::::NoPermission); - details.is_frozen = true; + CollectionConfigOf::::try_mutate(collection, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; + let mut settings = config.values(); + settings.insert(CollectionSetting::NonTransferableItems); + config.0 = settings; Self::deposit_event(Event::::CollectionFrozen { collection }); Ok(()) @@ -1001,20 +1005,17 @@ pub mod pallet { let delegate = T::Lookup::lookup(delegate)?; - let config = CollectionConfigOf::::get(collection) - .ok_or(Error::::UnknownCollection)?; - - let settings = config.values(); - ensure!( - !settings.contains(CollectionSetting::NonTransferableItems), - Error::::ItemsNotTransferable - ); - let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; + let action_allowed = Self::is_collection_setting_disabled( + &collection, + CollectionSetting::NonTransferableItems, + )?; + ensure!(action_allowed, Error::::ItemsNotTransferable); + if let Some(check) = maybe_check { let permitted = check == collection_details.admin || check == details.owner; ensure!(permitted, Error::::NoPermission); @@ -1156,8 +1157,7 @@ pub mod pallet { /// - `admin`: The new Admin of this collection. /// - `freezer`: The new Freezer of this collection. /// - `free_holding`: Whether a deposit is taken for holding an item in this collection. - /// - `is_frozen`: Whether this collection is frozen except for permissioned/admin - /// instructions. + /// - `config`: Collection's config. /// /// Emits `CollectionStatusChanged` with the identity of the item. /// @@ -1171,7 +1171,7 @@ pub mod pallet { admin: AccountIdLookupOf, freezer: AccountIdLookupOf, free_holding: bool, - is_frozen: bool, + config: CollectionConfig, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -1185,10 +1185,10 @@ pub mod pallet { collection.admin = T::Lookup::lookup(admin)?; collection.freezer = T::Lookup::lookup(freezer)?; collection.free_holding = free_holding; - collection.is_frozen = is_frozen; *maybe_collection = Some(collection); CollectionAccount::::remove(&old_owner, &collection_id); CollectionAccount::::insert(&new_owner, &collection_id, ()); + CollectionConfigOf::::insert(&collection_id, config); Self::deposit_event(Event::CollectionStatusChanged { collection: collection_id }); Ok(()) @@ -1445,14 +1445,14 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - let config = CollectionConfigOf::::get(collection) + /*let config = CollectionConfigOf::::get(collection) .ok_or(Error::::UnknownCollection)?; let settings = config.values(); ensure!( !settings.contains(CollectionSetting::LockedMetadata), Error::::CollectionIsLocked - ); + );*/ let mut details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 66893b300405a..6a680289ced38 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -245,7 +245,10 @@ fn freezing_should_work() { assert_ok!(Nfts::thaw(RuntimeOrigin::signed(1), 0, 42)); assert_ok!(Nfts::freeze_collection(RuntimeOrigin::signed(1), 0)); - assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); + assert_noop!( + Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), + Error::::ItemsNotTransferable + ); assert_ok!(Nfts::force_collection_status( RuntimeOrigin::root(), @@ -1137,7 +1140,7 @@ fn buy_item_should_work() { Error::::NotForSale ); - // ensure we can't buy an item when the collection or an item is frozen + // ensure we can't buy an item when the collection or an item are frozen { assert_ok!(Nfts::set_price( RuntimeOrigin::signed(user_1), @@ -1147,7 +1150,7 @@ fn buy_item_should_work() { None, )); - // freeze collection + // freeze the collection assert_ok!(Nfts::freeze_collection(RuntimeOrigin::signed(user_1), collection_id)); let buy_item_call = mock::RuntimeCall::Nfts(crate::Call::::buy_item { @@ -1157,10 +1160,22 @@ fn buy_item_should_work() { }); assert_noop!( buy_item_call.dispatch(RuntimeOrigin::signed(user_2)), - Error::::Frozen + Error::::ItemsNotTransferable ); - // freeze item + // un-freeze the collection + assert_ok!(Nfts::force_collection_status( + RuntimeOrigin::root(), + collection_id, + user_1, + user_1, + user_1, + user_1, + false, + CollectionConfig::empty(), + )); + + // freeze the item assert_ok!(Nfts::freeze(RuntimeOrigin::signed(user_1), collection_id, item_3)); let buy_item_call = mock::RuntimeCall::Nfts(crate::Call::::buy_item { @@ -1173,22 +1188,6 @@ fn buy_item_should_work() { Error::::Frozen ); } - - // ensure we can't buy an item when the collection has a NonTransferableItems flag - let collection_id = 1; - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_1, - CollectionConfig(CollectionSetting::NonTransferableItems.into()), - true - )); - - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1)); - - assert_noop!( - Nfts::buy_item(RuntimeOrigin::signed(user_2), collection_id, item_1, price_1.into()), - Error::::NotForSale - ); }); } diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index fad6b505d940f..7bf027968a879 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -63,8 +63,6 @@ pub struct CollectionDetails { pub(super) item_metadatas: u32, /// The total number of attributes for this collection. pub(super) attributes: u32, - /// Whether the collection is frozen for non-admin transfers. - pub(super) is_frozen: bool, } /// Witness data for the destroy transactions. From a9ab6f88a8b20fccee057ad3b126bca6fc33ba2f Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 11:59:27 +0300 Subject: [PATCH 18/63] Allow to lock the collection in a new way --- frame/nfts/src/benchmarking.rs | 11 ++- frame/nfts/src/functions.rs | 26 ------- frame/nfts/src/lib.rs | 43 ++++++----- frame/nfts/src/tests.rs | 135 ++++++++++++++++++++------------- frame/nfts/src/weights.rs | 6 +- 5 files changed, 114 insertions(+), 107 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 26807a24cbc66..293c665ce33de 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -240,11 +240,16 @@ benchmarks_instance_pallet! { assert_last_event::(Event::Thawed { collection, item }.into()); } - freeze_collection { + lock_collection { let (collection, caller, caller_lookup) = create_collection::(); - }: _(SystemOrigin::Signed(caller.clone()), collection) + let lock_config = CollectionConfig( + CollectionSetting::NonTransferableItems | + CollectionSetting::LockedMetadata | + CollectionSetting::LockedAttributes, + ); + }: _(SystemOrigin::Signed(caller.clone()), collection, lock_config) verify { - assert_last_event::(Event::CollectionFrozen { collection }.into()); + assert_last_event::(Event::CollectionLocked { collection }.into()); } transfer_ownership { diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 837e7ddaa07e2..85d7c14328eb1 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -303,32 +303,6 @@ impl, I: 'static> Pallet { Ok(()) } - /*pub fn do_change_collection_config( - id: T::CollectionId, - caller: T::AccountId, - current_config: CollectionConfig, - new_config: CollectionConfig, - ) -> DispatchResult { - let collection = Collection::::get(id).ok_or(Error::::UnknownCollection)?; - ensure!(collection.owner == caller, Error::::NoPermission); - - let settings = current_config.values(); - - if settings.contains(CollectionSetting::IsLocked) { - return Err(Error::::CollectionIsLocked.into()) - } - - CollectionConfigOf::::try_mutate(id, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; - - config.user_features = new_config; - - Self::deposit_event(Event::::CollectionConfigChanged { id }); - - Ok(()) - }) - }*/ - #[cfg(any(test, feature = "runtime-benchmarks"))] pub fn set_next_id(id: T::CollectionId) { NextCollectionId::::set(Some(id)); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index af413fa439e0e..643d482357a3e 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -344,7 +344,7 @@ pub mod pallet { /// Some `item` was thawed. Thawed { collection: T::CollectionId, item: T::ItemId }, /// Some `collection` was frozen. - CollectionFrozen { collection: T::CollectionId }, + CollectionLocked { collection: T::CollectionId }, /// The owner changed. OwnerChanged { collection: T::CollectionId, new_owner: T::AccountId }, /// The management team changed. @@ -582,19 +582,6 @@ pub mod pallet { ) } - /*#[pallet::weight(0)] - pub fn change_collection_config( - origin: OriginFor, - id: T::CollectionId, - new_config: CollectionConfig, - ) -> DispatchResult { - let sender = ensure_signed(origin)?; - let current_config = - CollectionConfigOf::::get(id).ok_or(Error::::UnknownCollection)?; - Self::do_change_collection_config(id, sender, current_config, new_config)?; - Ok(()) - }*/ - /// Destroy a collection of fungible items. /// /// The origin must conform to `ForceOrigin` or must be `Signed` and the sender must be the @@ -862,19 +849,22 @@ pub mod pallet { Ok(()) } - /// Disallow further unprivileged transfers for a whole collection. + /// Disallows specified settings for the whole collection. /// /// Origin must be Signed and the sender should be the Freezer of the `collection`. /// - /// - `collection`: The collection to be frozen. + /// - `collection`: The collection to be locked. + /// - `lock_config`: The config with the settings to be locked. /// - /// Emits `CollectionFrozen`. + /// Note: it's possible to only lock(set) the setting, but not to unset it. + /// Emits `CollectionLocked`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::freeze_collection())] - pub fn freeze_collection( + #[pallet::weight(T::WeightInfo::lock_collection())] + pub fn lock_collection( origin: OriginFor, collection: T::CollectionId, + lock_config: CollectionConfig, ) -> DispatchResult { let origin = ensure_signed(origin)?; @@ -885,10 +875,21 @@ pub mod pallet { CollectionConfigOf::::try_mutate(collection, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; let mut settings = config.values(); - settings.insert(CollectionSetting::NonTransferableItems); + let lock_settings = lock_config.values(); + + if lock_settings.contains(CollectionSetting::NonTransferableItems) { + settings.insert(CollectionSetting::NonTransferableItems); + } + if lock_settings.contains(CollectionSetting::LockedMetadata) { + settings.insert(CollectionSetting::LockedMetadata); + } + if lock_settings.contains(CollectionSetting::LockedAttributes) { + settings.insert(CollectionSetting::LockedAttributes); + } + config.0 = settings; - Self::deposit_event(Event::::CollectionFrozen { collection }); + Self::deposit_event(Event::::CollectionLocked { collection }); Ok(()) }) } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 6a680289ced38..bc093d3c4081d 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -110,30 +110,6 @@ fn basic_minting_should_work() { }); } -/*#[test] -fn collection_locking_should_work() { - new_test_ext().execute_with(|| { - let user_id = 1; - let collection_id = 0; - - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id, - CollectionConfig::empty(), - true - )); - - let new_config = CollectionConfig(CollectionSetting::IsLocked.into()); - - assert_ok!(Nfts::change_collection_config(RuntimeOrigin::signed(user_id), collection_id, new_config)); - - let collection_config = CollectionConfigOf::::get(collection_id); - - let expected_config = CollectionConfig(new_config); - assert_eq!(Some(expected_config), collection_config); - }); -}*/ - #[test] fn lifecycle_should_work() { new_test_ext().execute_with(|| { @@ -244,7 +220,11 @@ fn freezing_should_work() { assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); assert_ok!(Nfts::thaw(RuntimeOrigin::signed(1), 0, 42)); - assert_ok!(Nfts::freeze_collection(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(1), + 0, + CollectionConfig(CollectionSetting::NonTransferableItems.into()) + )); assert_noop!( Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::ItemsNotTransferable @@ -1150,8 +1130,12 @@ fn buy_item_should_work() { None, )); - // freeze the collection - assert_ok!(Nfts::freeze_collection(RuntimeOrigin::signed(user_1), collection_id)); + // lock the collection + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(user_1), + collection_id, + CollectionConfig(CollectionSetting::NonTransferableItems.into()) + )); let buy_item_call = mock::RuntimeCall::Nfts(crate::Call::::buy_item { collection: collection_id, @@ -1191,33 +1175,6 @@ fn buy_item_should_work() { }); } -#[test] -fn various_collection_settings() { - new_test_ext().execute_with(|| { - // when we set only one value it's required to call .into() on it - let config = CollectionConfig(CollectionSetting::NonTransferableItems.into()); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); - - let config = CollectionConfigOf::::get(0).unwrap(); - let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); - assert!(!stored_settings.contains(CollectionSetting::LockedMetadata)); - - // no need to call .into() for multiple values - let config = CollectionConfig( - CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, - ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); - - let config = CollectionConfigOf::::get(1).unwrap(); - let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); - assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); - - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); - }); -} - #[test] fn pay_tips_should_work() { new_test_ext().execute_with(|| { @@ -1262,3 +1219,73 @@ fn pay_tips_should_work() { })); }); } + +#[test] +fn various_collection_settings() { + new_test_ext().execute_with(|| { + // when we set only one value it's required to call .into() on it + let config = CollectionConfig(CollectionSetting::NonTransferableItems.into()); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); + + let config = CollectionConfigOf::::get(0).unwrap(); + let stored_settings = config.values(); + assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); + assert!(!stored_settings.contains(CollectionSetting::LockedMetadata)); + + // no need to call .into() for multiple values + let config = CollectionConfig( + CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, + ); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); + + let config = CollectionConfigOf::::get(1).unwrap(); + let stored_settings = config.values(); + assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); + assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); + + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + }); +} + +#[test] +fn collection_locking_should_work() { + new_test_ext().execute_with(|| { + let user_id = 1; + let collection_id = 0; + + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id, + CollectionConfig::empty(), + false + )); + + // validate partial lock + let lock_config = CollectionConfig( + CollectionSetting::NonTransferableItems | CollectionSetting::LockedAttributes, + ); + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(user_id), + collection_id, + lock_config + )); + + let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); + assert_eq!(stored_config, lock_config); + + // validate full lock + let full_lock_config = CollectionConfig( + CollectionSetting::NonTransferableItems | + CollectionSetting::LockedMetadata | + CollectionSetting::LockedAttributes, + ); + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(user_id), + collection_id, + CollectionConfig(CollectionSetting::LockedMetadata.into()), + )); + + let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); + assert_eq!(stored_config, full_lock_config); + }); +} diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index b92f1fce0deb0..27d770e7f9344 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -55,7 +55,7 @@ pub trait WeightInfo { fn redeposit(i: u32, ) -> Weight; fn freeze() -> Weight; fn thaw() -> Weight; - fn freeze_collection() -> Weight; + fn lock_collection() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn force_collection_status() -> Weight; @@ -172,7 +172,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) - fn freeze_collection() -> Weight { + fn lock_collection() -> Weight { Weight::from_ref_time(23_129_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) @@ -396,7 +396,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) - fn freeze_collection() -> Weight { + fn lock_collection() -> Weight { Weight::from_ref_time(23_129_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) From 254816dce2540ef0372f2c950334b3d0c909d4e6 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 18:14:19 +0300 Subject: [PATCH 19/63] Move free_holding into settings --- frame/nfts/src/benchmarking.rs | 3 +- frame/nfts/src/functions.rs | 8 +- frame/nfts/src/impl_nonfungibles.rs | 10 ++- frame/nfts/src/lib.rs | 40 +++++++--- frame/nfts/src/tests.rs | 112 +++++++++++++--------------- frame/nfts/src/types.rs | 7 +- 6 files changed, 98 insertions(+), 82 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 293c665ce33de..0956158082ef4 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -288,8 +288,7 @@ benchmarks_instance_pallet! { issuer: caller_lookup.clone(), admin: caller_lookup.clone(), freezer: caller_lookup, - free_holding: true, - config: CollectionConfig::empty(), + config: CollectionConfig(CollectionSetting::FreeHolding), }; }: { call.dispatch_bypass_filter(origin)? } verify { diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 85d7c14328eb1..ecd06ddc0903e 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -77,7 +77,6 @@ impl, I: 'static> Pallet { admin: T::AccountId, config: CollectionConfig, deposit: DepositBalanceOf, - free_holding: bool, event: Event, ) -> DispatchResult { ensure!(!Collection::::contains_key(collection), Error::::CollectionIdInUse); @@ -92,7 +91,6 @@ impl, I: 'static> Pallet { admin: admin.clone(), freezer: admin, total_deposit: deposit, - free_holding, items: 0, item_metadatas: 0, attributes: 0, @@ -177,7 +175,11 @@ impl, I: 'static> Pallet { collection_details.items.checked_add(1).ok_or(ArithmeticError::Overflow)?; collection_details.items = items; - let deposit = match collection_details.free_holding { + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); + + let deposit = match settings.contains(CollectionSetting::FreeHolding) { true => Zero::zero(), false => T::ItemDeposit::get(), }; diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 6b0d5a90fccac..7efeacbb07bcc 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -96,15 +96,19 @@ impl, I: 'static> Create<::AccountId, Collection collection: &Self::CollectionId, who: &T::AccountId, admin: &T::AccountId, - config: &CollectionSettings, + settings: &CollectionSettings, ) -> DispatchResult { + let mut settings = *settings; + // FreeHolding could be set by calling the force_create() only + if settings.contains(CollectionSetting::FreeHolding) { + settings.remove(CollectionSetting::FreeHolding); + } Self::do_create_collection( *collection, who.clone(), admin.clone(), - CollectionConfig(*config), + CollectionConfig(settings), T::CollectionDeposit::get(), - false, Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, ) } diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 643d482357a3e..669247303db94 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -531,13 +531,19 @@ pub mod pallet { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; let admin = T::Lookup::lookup(admin)?; + let mut settings = config.values(); + // FreeHolding could be set by calling the force_create() only + if settings.contains(CollectionSetting::FreeHolding) { + settings.remove(CollectionSetting::FreeHolding); + } + let config = CollectionConfig(settings); + Self::do_create_collection( collection, owner.clone(), admin.clone(), config, T::CollectionDeposit::get(), - false, Event::Created { collection, creator: owner, owner: admin }, ) } @@ -563,7 +569,6 @@ pub mod pallet { origin: OriginFor, owner: AccountIdLookupOf, config: CollectionConfig, - free_holding: bool, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; let owner = T::Lookup::lookup(owner)?; @@ -577,7 +582,6 @@ pub mod pallet { owner.clone(), config, Zero::zero(), - free_holding, Event::ForceCreated { collection, owner }, ) } @@ -748,7 +752,12 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.owner == origin, Error::::NoPermission); - let deposit = match collection_details.free_holding { + + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); + + let deposit = match settings.contains(CollectionSetting::FreeHolding) { true => Zero::zero(), false => T::ItemDeposit::get(), }; @@ -1157,7 +1166,6 @@ pub mod pallet { /// - `issuer`: The new Issuer of this collection. /// - `admin`: The new Admin of this collection. /// - `freezer`: The new Freezer of this collection. - /// - `free_holding`: Whether a deposit is taken for holding an item in this collection. /// - `config`: Collection's config. /// /// Emits `CollectionStatusChanged` with the identity of the item. @@ -1171,7 +1179,6 @@ pub mod pallet { issuer: AccountIdLookupOf, admin: AccountIdLookupOf, freezer: AccountIdLookupOf, - free_holding: bool, config: CollectionConfig, ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; @@ -1185,7 +1192,6 @@ pub mod pallet { collection.issuer = T::Lookup::lookup(issuer)?; collection.admin = T::Lookup::lookup(admin)?; collection.freezer = T::Lookup::lookup(freezer)?; - collection.free_holding = free_holding; *maybe_collection = Some(collection); CollectionAccount::::remove(&old_owner, &collection_id); CollectionAccount::::insert(&new_owner, &collection_id, ()); @@ -1227,6 +1233,11 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); + if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } @@ -1243,7 +1254,7 @@ pub mod pallet { let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !collection_details.free_holding && maybe_check_owner.is_some() { + if !settings.contains(CollectionSetting::FreeHolding) && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() .saturating_mul(((key.len() + value.len()) as u32).into()) .saturating_add(T::AttributeDepositBase::get()); @@ -1339,6 +1350,10 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); + if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } @@ -1353,7 +1368,8 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !collection_details.free_holding && maybe_check_owner.is_some() { + if !settings.contains(CollectionSetting::FreeHolding) && maybe_check_owner.is_some() + { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); @@ -1454,6 +1470,9 @@ pub mod pallet { !settings.contains(CollectionSetting::LockedMetadata), Error::::CollectionIsLocked );*/ + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); let mut details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; @@ -1468,7 +1487,8 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if maybe_check_owner.is_some() && !details.free_holding { + if maybe_check_owner.is_some() && !settings.contains(CollectionSetting::FreeHolding) + { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) .saturating_add(T::MetadataDepositBase::get()); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index bc093d3c4081d..96852059f282c 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -88,6 +88,10 @@ fn events() -> Vec> { result } +fn default_config() -> CollectionConfig { + CollectionConfig(CollectionSetting::FreeHolding.into()) +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -98,12 +102,12 @@ fn basic_setup_works() { #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_eq!(items(), vec![(1, 0, 42)]); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, default_config())); assert_eq!(collections(), vec![(1, 0), (2, 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 1, 69, 1)); assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); @@ -114,7 +118,7 @@ fn basic_minting_should_work() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0], false)); @@ -158,7 +162,7 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); @@ -169,7 +173,7 @@ fn destroy_with_bad_witness_should_not_work() { #[test] fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_eq!(Nfts::owner(0, 42).unwrap(), 1); assert_eq!(collections(), vec![(1, 0)]); @@ -180,7 +184,7 @@ fn mint_should_work() { #[test] fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 3)); @@ -198,8 +202,9 @@ fn transfer_should_work() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig(CollectionSetting::NonTransferableItems.into()), - true + CollectionConfig( + CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + ) )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, 1, 42)); @@ -214,7 +219,7 @@ fn transfer_should_work() { #[test] fn freezing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_ok!(Nfts::freeze(RuntimeOrigin::signed(1), 0, 42)); assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); @@ -237,7 +242,6 @@ fn freezing_should_work() { 1, 1, 1, - false, CollectionConfig::empty(), )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2)); @@ -247,7 +251,7 @@ fn freezing_should_work() { #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&2, 100); @@ -278,7 +282,7 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, CollectionConfig::empty())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); assert_eq!(collections(), vec![(1, 0)]); assert_noop!( Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2), @@ -328,7 +332,7 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2)); @@ -347,7 +351,7 @@ fn set_collection_metadata_should_work() { Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20], false), Error::::UnknownCollection, ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); // Cannot add metadata to unowned item assert_noop!( Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20], false), @@ -427,7 +431,7 @@ fn set_item_metadata_should_work() { Balances::make_free_balance_be(&1, 30); // Cannot add metadata to unknown item - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); // Cannot add metadata to unowned item assert_noop!( @@ -494,7 +498,7 @@ fn set_attribute_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -539,7 +543,7 @@ fn set_attribute_should_respect_freeze() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -574,7 +578,7 @@ fn force_collection_status_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2)); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20], false)); @@ -590,8 +594,7 @@ fn force_collection_status_should_work() { 1, 1, 1, - true, - CollectionConfig::empty(), + CollectionConfig(CollectionSetting::FreeHolding.into()), )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2)); @@ -617,7 +620,7 @@ fn force_collection_status_should_work() { fn burn_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); assert_noop!( @@ -647,7 +650,7 @@ fn burn_works() { #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4)); @@ -665,8 +668,9 @@ fn approval_lifecycle_works() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig(CollectionSetting::NonTransferableItems.into()), - true + CollectionConfig( + CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + ) )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, collection_id, 1)); @@ -681,7 +685,7 @@ fn approval_lifecycle_works() { #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -728,7 +732,7 @@ fn cancel_approval_works() { #[test] fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); let current_block = 1; @@ -753,7 +757,7 @@ fn approving_multiple_accounts_works() { #[test] fn approvals_limit_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); for i in 3..13 { @@ -773,7 +777,11 @@ fn approval_deadline_works() { System::set_block_number(0); assert!(System::block_number().is_zero()); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig(CollectionSetting::FreeHolding.into()) + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); // the approval expires after the 2nd block. @@ -800,7 +808,7 @@ fn approval_deadline_works() { #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -828,7 +836,7 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -856,7 +864,7 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -895,12 +903,7 @@ fn max_supply_should_work() { let max_supply = 2; // validate set_collection_max_supply - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_config())); assert!(!CollectionMaxSupply::::contains_key(collection_id)); assert_ok!(Nfts::set_collection_max_supply( @@ -950,12 +953,7 @@ fn set_price_should_work() { let item_1 = 1; let item_2 = 2; - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_2, user_id)); @@ -1010,8 +1008,9 @@ fn set_price_should_work() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), user_id, - CollectionConfig(CollectionSetting::NonTransferableItems.into()), - true + CollectionConfig( + CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + ) )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); @@ -1041,12 +1040,7 @@ fn buy_item_should_work() { Balances::make_free_balance_be(&user_2, initial_balance); Balances::make_free_balance_be(&user_3, initial_balance); - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_1, - CollectionConfig::empty(), - true - )); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_1)); @@ -1155,7 +1149,6 @@ fn buy_item_should_work() { user_1, user_1, user_1, - false, CollectionConfig::empty(), )); @@ -1225,7 +1218,7 @@ fn various_collection_settings() { new_test_ext().execute_with(|| { // when we set only one value it's required to call .into() on it let config = CollectionConfig(CollectionSetting::NonTransferableItems.into()); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); let config = CollectionConfigOf::::get(0).unwrap(); let stored_settings = config.values(); @@ -1236,14 +1229,14 @@ fn various_collection_settings() { let config = CollectionConfig( CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config, false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); let config = CollectionConfigOf::::get(1).unwrap(); let stored_settings = config.values(); assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty(), false)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); }); } @@ -1253,12 +1246,7 @@ fn collection_locking_should_work() { let user_id = 1; let collection_id = 0; - assert_ok!(Nfts::force_create( - RuntimeOrigin::root(), - user_id, - CollectionConfig::empty(), - false - )); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, CollectionConfig::empty())); // validate partial lock let lock_config = CollectionConfig( @@ -1267,7 +1255,7 @@ fn collection_locking_should_work() { assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(user_id), collection_id, - lock_config + lock_config, )); let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 7bf027968a879..77136cd0df55a 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -55,8 +55,6 @@ pub struct CollectionDetails { /// The total balance deposited for the all storage associated with this collection. /// Used by `destroy`. pub(super) total_deposit: DepositBalance, - /// If `true`, then no deposit is needed to hold items of this collection. - pub(super) free_holding: bool, /// The total number of outstanding items of this collection. pub(super) items: u32, /// The total number of outstanding item metadata of this collection. @@ -152,9 +150,14 @@ pub struct ItemTip { #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum CollectionSetting { + /// Disallow to transfer items in this collection. NonTransferableItems, + /// Disallow to modify metadata of this collection. LockedMetadata, + /// Disallow to modify attributes of this collection. LockedAttributes, + /// When this is set then no deposit needed to hold items of this collection. + FreeHolding, } pub(super) type CollectionSettings = BitFlags; From 5878197efe2ecc982bb839a3163daa7084a10c9c Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 26 Sep 2022 18:58:40 +0300 Subject: [PATCH 20/63] Connect collection's metadata locker to feature flags --- frame/nfts/src/benchmarking.rs | 4 +- frame/nfts/src/features/common.rs | 4 +- frame/nfts/src/functions.rs | 6 +-- frame/nfts/src/lib.rs | 61 ++++++++++++++------------- frame/nfts/src/tests.rs | 70 +++++++++++++------------------ frame/nfts/src/types.rs | 4 +- 6 files changed, 69 insertions(+), 80 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 0956158082ef4..8ce967b476dec 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -340,9 +340,9 @@ benchmarks_instance_pallet! { let data: BoundedVec<_, _> = vec![0u8; T::StringLimit::get() as usize].try_into().unwrap(); let (collection, caller, _) = create_collection::(); - }: _(SystemOrigin::Signed(caller), collection, data.clone(), false) + }: _(SystemOrigin::Signed(caller), collection, data.clone()) verify { - assert_last_event::(Event::CollectionMetadataSet { collection, data, is_frozen: false }.into()); + assert_last_event::(Event::CollectionMetadataSet { collection, data }.into()); } clear_collection_metadata { diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/common.rs index 48db43210e097..12cd6758aa4b2 100644 --- a/frame/nfts/src/features/common.rs +++ b/frame/nfts/src/features/common.rs @@ -22,11 +22,11 @@ impl, I: 'static> Pallet { pub fn is_collection_setting_disabled( collection_id: &T::CollectionId, setting: CollectionSetting, - ) -> Result { + ) -> Result<(bool, CollectionSettings), DispatchError> { let config = CollectionConfigOf::::get(&collection_id) .ok_or(Error::::UnknownCollection)?; let settings = config.values(); - Ok(!settings.contains(setting)) + Ok((!settings.contains(setting), settings)) } } diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index ecd06ddc0903e..521de4aba62b7 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -38,14 +38,14 @@ impl, I: 'static> Pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); - let action_allowed = Self::is_collection_setting_disabled( + let (action_allowed, _) = Self::is_collection_setting_disabled( &collection, CollectionSetting::NonTransferableItems, )?; ensure!(action_allowed, Error::::ItemsNotTransferable); let mut details = - Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; + Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(!details.is_frozen, Error::::Frozen); with_details(&collection_details, &mut details)?; @@ -243,7 +243,7 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner == sender, Error::::NoPermission); - let action_allowed = Self::is_collection_setting_disabled( + let (action_allowed, _) = Self::is_collection_setting_disabled( &collection, CollectionSetting::NonTransferableItems, )?; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 669247303db94..443e81b0c3c57 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -376,11 +376,7 @@ pub mod pallet { /// A `collection` has had its attributes changed by the `Force` origin. CollectionStatusChanged { collection: T::CollectionId }, /// New metadata has been set for a `collection`. - CollectionMetadataSet { - collection: T::CollectionId, - data: BoundedVec, - is_frozen: bool, - }, + CollectionMetadataSet { collection: T::CollectionId, data: BoundedVec }, /// Metadata has been cleared for a `collection`. CollectionMetadataCleared { collection: T::CollectionId }, /// New metadata has been set for an item. @@ -472,8 +468,10 @@ pub mod pallet { Unaccepted, /// The item is locked. Locked, - /// The collection is locked. - CollectionIsLocked, + /// The collection's metadata is locked. + CollectionMetadataIsLocked, + /// The collection's attributes are locked. + CollectionAttributesAreLocked, /// All items have been minted. MaxSupplyReached, /// The max supply has already been set. @@ -1020,7 +1018,7 @@ pub mod pallet { let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; - let action_allowed = Self::is_collection_setting_disabled( + let (action_allowed, _) = Self::is_collection_setting_disabled( &collection, CollectionSetting::NonTransferableItems, )?; @@ -1242,7 +1240,7 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } let maybe_is_frozen = match maybe_item { - None => CollectionMetadataOf::::get(collection).map(|v| v.is_frozen), + None => Some(settings.contains(CollectionSetting::LockedAttributes)), Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); @@ -1302,8 +1300,13 @@ pub mod pallet { if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } + + let config = CollectionConfigOf::::get(&collection) + .ok_or(Error::::UnknownCollection)?; + let settings = config.values(); + let maybe_is_frozen = match maybe_item { - None => CollectionMetadataOf::::get(collection).map(|v| v.is_frozen), + None => Some(settings.contains(CollectionSetting::LockedAttributes)), Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); @@ -1446,7 +1449,6 @@ pub mod pallet { /// /// - `collection`: The identifier of the item whose metadata to update. /// - `data`: The general information of this item. Limited in length by `StringLimit`. - /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `CollectionMetadataSet`. /// @@ -1456,23 +1458,19 @@ pub mod pallet { origin: OriginFor, collection: T::CollectionId, data: BoundedVec, - is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - /*let config = CollectionConfigOf::::get(collection) - .ok_or(Error::::UnknownCollection)?; - - let settings = config.values(); + let (action_allowed, settings) = Self::is_collection_setting_disabled( + &collection, + CollectionSetting::LockedMetadata, + )?; ensure!( - !settings.contains(CollectionSetting::LockedMetadata), - Error::::CollectionIsLocked - );*/ - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); + maybe_check_owner.is_none() || action_allowed, + Error::::CollectionMetadataIsLocked + ); let mut details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; @@ -1481,9 +1479,6 @@ pub mod pallet { } CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { - let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); - ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); - let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); @@ -1502,9 +1497,9 @@ pub mod pallet { Collection::::insert(&collection, details); - *metadata = Some(CollectionMetadata { deposit, data: data.clone(), is_frozen }); + *metadata = Some(CollectionMetadata { deposit, data: data.clone() }); - Self::deposit_event(Event::CollectionMetadataSet { collection, data, is_frozen }); + Self::deposit_event(Event::CollectionMetadataSet { collection, data }); Ok(()) }) } @@ -1536,10 +1531,16 @@ pub mod pallet { ensure!(check_owner == &details.owner, Error::::NoPermission); } - CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { - let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); - ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + let (action_allowed, _) = Self::is_collection_setting_disabled( + &collection, + CollectionSetting::LockedMetadata, + )?; + ensure!( + maybe_check_owner.is_none() || action_allowed, + Error::::CollectionMetadataIsLocked + ); + CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { let deposit = metadata.take().ok_or(Error::::UnknownCollection)?.deposit; T::Currency::unreserve(&details.owner, deposit); Self::deposit_event(Event::CollectionMetadataCleared { collection }); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 96852059f282c..b279a7fa2c24a 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -121,7 +121,7 @@ fn lifecycle_should_work() { assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0], false)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0])); assert_eq!(Balances::reserved_balance(&1), 5); assert!(CollectionMetadataOf::::contains_key(0)); @@ -304,12 +304,7 @@ fn transfer_owner_should_work() { ); // Mint and set metadata now and make sure that deposit gets transferred back. - assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(2), - 0, - bvec![0u8; 20], - false - )); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20])); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20], false)); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(3), Some(0))); @@ -348,70 +343,55 @@ fn set_collection_metadata_should_work() { new_test_ext().execute_with(|| { // Cannot add metadata to unknown item assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20], false), + Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20]), Error::::UnknownCollection, ); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); // Cannot add metadata to unowned item assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20], false), + Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20]), Error::::NoPermission, ); // Successfully add metadata and take deposit Balances::make_free_balance_be(&1, 30); - assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(1), - 0, - bvec![0u8; 20], - false - )); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20])); assert_eq!(Balances::free_balance(&1), 9); assert!(CollectionMetadataOf::::contains_key(0)); // Force origin works, too. - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 18], false)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 18])); // Update deposit - assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(1), - 0, - bvec![0u8; 15], - false - )); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15])); assert_eq!(Balances::free_balance(&1), 14); - assert_ok!(Nfts::set_collection_metadata( - RuntimeOrigin::signed(1), - 0, - bvec![0u8; 25], - false - )); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 25])); assert_eq!(Balances::free_balance(&1), 4); // Cannot over-reserve assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 40], false), + Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 40]), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Nfts::set_collection_metadata( + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15])); + assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(1), 0, - bvec![0u8; 15], - true + CollectionConfig(CollectionSetting::LockedMetadata.into()) )); assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15], false), - Error::::Frozen, + Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15]), + Error::::CollectionMetadataIsLocked, ); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), - Error::::Frozen + Error::::CollectionMetadataIsLocked ); // Clear Metadata - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 15], false)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::root(), 0, bvec![0u8; 15])); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(2), 0), Error::::NoPermission @@ -420,7 +400,11 @@ fn set_collection_metadata_should_work() { Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 1), Error::::UnknownCollection ); - assert_ok!(Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0)); + assert_noop!( + Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), + Error::::CollectionMetadataIsLocked + ); + assert_ok!(Nfts::clear_collection_metadata(RuntimeOrigin::root(), 0)); assert!(!CollectionMetadataOf::::contains_key(0)); }); } @@ -558,7 +542,13 @@ fn set_attribute_should_respect_freeze() { ); assert_eq!(Balances::reserved_balance(1), 9); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![], true)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![])); + assert_ok!(Nfts::lock_collection( + RuntimeOrigin::signed(1), + 0, + CollectionConfig(CollectionSetting::LockedAttributes.into()) + )); + let e = Error::::Frozen; assert_noop!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0]), e); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1])); @@ -581,7 +571,7 @@ fn force_collection_status_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2)); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20], false)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20], false)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 65); @@ -611,7 +601,7 @@ fn force_collection_status_should_work() { assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 21); - assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20], false)); + assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); assert_eq!(Balances::reserved_balance(1), 0); }); } diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 77136cd0df55a..28c62a8611adb 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -113,8 +113,6 @@ pub struct CollectionMetadata> { /// will generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. pub(super) data: BoundedVec, - /// Whether the collection's metadata may be changed by a non Force origin. - pub(super) is_frozen: bool, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, Default, TypeInfo, MaxEncodedLen)] @@ -156,7 +154,7 @@ pub enum CollectionSetting { LockedMetadata, /// Disallow to modify attributes of this collection. LockedAttributes, - /// When this is set then no deposit needed to hold items of this collection. + /// When is set then no deposit needed to hold items of this collection. FreeHolding, } From 95e21fbba73a11c7cf03f612f107eb529cd9ac77 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 10:46:43 +0300 Subject: [PATCH 21/63] DRY --- frame/nfts/src/features/common.rs | 13 +++++++++---- frame/nfts/src/functions.rs | 4 +--- frame/nfts/src/lib.rs | 17 ++++------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/common.rs index 12cd6758aa4b2..3a46306a603b8 100644 --- a/frame/nfts/src/features/common.rs +++ b/frame/nfts/src/features/common.rs @@ -19,14 +19,19 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { - pub fn is_collection_setting_disabled( + pub fn get_collection_settings( collection_id: &T::CollectionId, - setting: CollectionSetting, - ) -> Result<(bool, CollectionSettings), DispatchError> { + ) -> Result { let config = CollectionConfigOf::::get(&collection_id) .ok_or(Error::::UnknownCollection)?; + Ok(config.values()) + } - let settings = config.values(); + pub fn is_collection_setting_disabled( + collection_id: &T::CollectionId, + setting: CollectionSetting, + ) -> Result<(bool, CollectionSettings), DispatchError> { + let settings = Self::get_collection_settings(&collection_id)?; Ok((!settings.contains(setting), settings)) } } diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 521de4aba62b7..aa029334b8a8a 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -175,9 +175,7 @@ impl, I: 'static> Pallet { collection_details.items.checked_add(1).ok_or(ArithmeticError::Overflow)?; collection_details.items = items; - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); + let settings = Self::get_collection_settings(&collection)?; let deposit = match settings.contains(CollectionSetting::FreeHolding) { true => Zero::zero(), diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 443e81b0c3c57..91be30495b3f0 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -751,10 +751,7 @@ pub mod pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.owner == origin, Error::::NoPermission); - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); - + let settings = Self::get_collection_settings(&collection)?; let deposit = match settings.contains(CollectionSetting::FreeHolding) { true => Zero::zero(), false => T::ItemDeposit::get(), @@ -1232,9 +1229,7 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); + let settings = Self::get_collection_settings(&collection)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); @@ -1301,9 +1296,7 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); + let settings = Self::get_collection_settings(&collection)?; let maybe_is_frozen = match maybe_item { None => Some(settings.contains(CollectionSetting::LockedAttributes)), @@ -1353,9 +1346,7 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - let config = CollectionConfigOf::::get(&collection) - .ok_or(Error::::UnknownCollection)?; - let settings = config.values(); + let settings = Self::get_collection_settings(&collection)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); From 17b7c9e300a4b8856a1d8d6f992da0bf3c533f7d Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 10:47:03 +0300 Subject: [PATCH 22/63] Chore --- frame/nfts/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 91be30495b3f0..d2862c8d14847 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -470,8 +470,6 @@ pub mod pallet { Locked, /// The collection's metadata is locked. CollectionMetadataIsLocked, - /// The collection's attributes are locked. - CollectionAttributesAreLocked, /// All items have been minted. MaxSupplyReached, /// The max supply has already been set. From dc6d2f043168fd457dfb1b3fa03ee6442c1c7a83 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 11:39:42 +0300 Subject: [PATCH 23/63] Connect pallet level feature flags --- bin/node/runtime/src/lib.rs | 2 +- frame/nfts/src/features/common.rs | 5 ++++ frame/nfts/src/lib.rs | 37 +++++++++++++++++++++++--- frame/nfts/src/mock.rs | 2 +- frame/nfts/src/types.rs | 43 +++++++++++++++++++++++++++++-- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4c5a4d748ae3c..0e52bd54dd912 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1498,7 +1498,7 @@ impl pallet_uniques::Config for Runtime { } parameter_types! { - pub FeatureFlags: SystemFeatures = SystemFeature::empty(); + pub FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); } impl pallet_nfts::Config for Runtime { diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/common.rs index 3a46306a603b8..805f254398c56 100644 --- a/frame/nfts/src/features/common.rs +++ b/frame/nfts/src/features/common.rs @@ -34,4 +34,9 @@ impl, I: 'static> Pallet { let settings = Self::get_collection_settings(&collection_id)?; Ok((!settings.contains(setting), settings)) } + + pub fn is_feature_flag_set(feature: SystemFeature) -> bool { + let features = T::FeatureFlags::get(); + return features.0.contains(feature) + } } diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index d2862c8d14847..34abf899dcd28 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -182,6 +182,8 @@ pub mod pallet { #[pallet::constant] type MaxTips: Get; + /// Disables some of pallet's features. + #[pallet::constant] type FeatureFlags: Get; #[cfg(feature = "runtime-benchmarks")] @@ -484,6 +486,8 @@ pub mod pallet { BidTooLow, /// The item has reached its approval limit. ReachedApprovalLimit, + /// The method is disabled by system settings. + MethodDisabled, } impl, I: 'static> Pallet { @@ -1002,6 +1006,10 @@ pub mod pallet { delegate: AccountIdLookupOf, maybe_deadline: Option<::BlockNumber>, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoApprovals), + Error::::MethodDisabled + ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; @@ -1066,6 +1074,10 @@ pub mod pallet { item: T::ItemId, delegate: AccountIdLookupOf, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoApprovals), + Error::::MethodDisabled + ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; @@ -1126,6 +1138,10 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoApprovals), + Error::::MethodDisabled + ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; @@ -1220,6 +1236,10 @@ pub mod pallet { key: BoundedVec, value: BoundedVec, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoAttributes), + Error::::MethodDisabled + ); let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; @@ -1227,11 +1247,11 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - let settings = Self::get_collection_settings(&collection)?; - if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } + + let settings = Self::get_collection_settings(&collection)?; let maybe_is_frozen = match maybe_item { None => Some(settings.contains(CollectionSetting::LockedAttributes)), Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), @@ -1284,6 +1304,10 @@ pub mod pallet { maybe_item: Option, key: BoundedVec, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoAttributes), + Error::::MethodDisabled + ); let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; @@ -1295,7 +1319,6 @@ pub mod pallet { } let settings = Self::get_collection_settings(&collection)?; - let maybe_is_frozen = match maybe_item { None => Some(settings.contains(CollectionSetting::LockedAttributes)), Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), @@ -1631,6 +1654,10 @@ pub mod pallet { whitelisted_buyer: Option>, ) -> DispatchResult { let origin = ensure_signed(origin)?; + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoTrading), + Error::::MethodDisabled + ); let whitelisted_buyer = whitelisted_buyer.map(T::Lookup::lookup).transpose()?; Self::do_set_price(collection, item, origin, price, whitelisted_buyer) } @@ -1653,6 +1680,10 @@ pub mod pallet { bid_price: ItemPrice, ) -> DispatchResult { let origin = ensure_signed(origin)?; + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoTrading), + Error::::MethodDisabled + ); Self::do_buy_item(collection, item, origin, bid_price) } diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 3e9e8ae631836..e4667a7265acb 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -86,7 +86,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub FeatureFlags: SystemFeatures = SystemFeature::empty(); + pub FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); } impl Config for Test { diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 28c62a8611adb..c2517adbc7f30 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -207,13 +207,52 @@ impl TypeInfo for CollectionConfig { // Support for up to 64 system-enabled features on a collection. #[bitflags] #[repr(u64)] -#[derive(Copy, Clone, RuntimeDebug, PartialEq)] +#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum SystemFeature { + /// Disallow trading operations. NoTrading, + /// Disallow setting attributes. NoAttributes, + /// Disallow transfer approvals. NoApprovals, + /// Disallow atomic items swap. NoSwaps, + /// Disallow public mints. NoPublicMints, } -pub type SystemFeatures = BitFlags; +pub type SystemFeatureFlags = BitFlags; + +/// Wrapper type for `SystemFeatureFlags` that implements `Codec`. +#[derive(Default, RuntimeDebug)] +pub struct SystemFeatures(pub SystemFeatureFlags); + +impl MaxEncodedLen for SystemFeatures { + fn max_encoded_len() -> usize { + u64::max_encoded_len() + } +} + +impl Encode for SystemFeatures { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } +} +impl EncodeLike for SystemFeatures {} +impl Decode for SystemFeatures { + fn decode(input: &mut I) -> sp_std::result::Result { + let field = u64::decode(input)?; + Ok(Self(SystemFeatureFlags::from_bits(field as u64).map_err(|_| "invalid value")?)) + } +} + +impl TypeInfo for SystemFeatures { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("SystemFeature"))) + } +} From cc64873ca4d6dff1ca5c2d9a023895aa1940b878 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 13:13:00 +0300 Subject: [PATCH 24/63] Prepare tests for the new changes --- frame/nfts/src/tests.rs | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index b279a7fa2c24a..04419c4f82ae7 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -88,7 +88,7 @@ fn events() -> Vec> { result } -fn default_config() -> CollectionConfig { +fn default_collection_config() -> CollectionConfig { CollectionConfig(CollectionSetting::FreeHolding.into()) } @@ -102,12 +102,12 @@ fn basic_setup_works() { #[test] fn basic_minting_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_eq!(items(), vec![(1, 0, 42)]); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, default_collection_config())); assert_eq!(collections(), vec![(1, 0), (2, 1)]); assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 1, 69, 1)); assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); @@ -118,7 +118,7 @@ fn basic_minting_should_work() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0])); @@ -162,7 +162,7 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); @@ -173,7 +173,7 @@ fn destroy_with_bad_witness_should_not_work() { #[test] fn mint_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_eq!(Nfts::owner(0, 42).unwrap(), 1); assert_eq!(collections(), vec![(1, 0)]); @@ -184,7 +184,7 @@ fn mint_should_work() { #[test] fn transfer_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 3)); @@ -219,7 +219,7 @@ fn transfer_should_work() { #[test] fn freezing_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); assert_ok!(Nfts::freeze(RuntimeOrigin::signed(1), 0, 42)); assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); @@ -251,7 +251,7 @@ fn freezing_should_work() { #[test] fn origin_guards_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); Balances::make_free_balance_be(&2, 100); @@ -282,7 +282,7 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_config())); + assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); assert_eq!(collections(), vec![(1, 0)]); assert_noop!( Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2), @@ -327,7 +327,7 @@ fn transfer_owner_should_work() { #[test] fn set_team_should_work() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2)); @@ -640,7 +640,7 @@ fn burn_works() { #[test] fn approval_lifecycle_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4)); @@ -675,7 +675,7 @@ fn approval_lifecycle_works() { #[test] fn cancel_approval_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -722,7 +722,7 @@ fn cancel_approval_works() { #[test] fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); let current_block = 1; @@ -747,7 +747,7 @@ fn approving_multiple_accounts_works() { #[test] fn approvals_limit_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); for i in 3..13 { @@ -798,7 +798,7 @@ fn approval_deadline_works() { #[test] fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -826,7 +826,7 @@ fn cancel_approval_works_with_admin() { #[test] fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -854,7 +854,7 @@ fn cancel_approval_works_with_force() { #[test] fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); @@ -893,7 +893,7 @@ fn max_supply_should_work() { let max_supply = 2; // validate set_collection_max_supply - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); assert!(!CollectionMaxSupply::::contains_key(collection_id)); assert_ok!(Nfts::set_collection_max_supply( @@ -943,7 +943,7 @@ fn set_price_should_work() { let item_1 = 1; let item_2 = 2; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_2, user_id)); @@ -1030,7 +1030,7 @@ fn buy_item_should_work() { Balances::make_free_balance_be(&user_2, initial_balance); Balances::make_free_balance_be(&user_3, initial_balance); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_1)); @@ -1226,7 +1226,7 @@ fn various_collection_settings() { assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_config())); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); }); } From 5153080a4ffe763f8d308974bd9b88551e289f60 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 13:22:06 +0300 Subject: [PATCH 25/63] Implement Item settings --- frame/nfts/src/features/common.rs | 18 ++ frame/nfts/src/functions.rs | 22 ++- frame/nfts/src/impl_nonfungibles.rs | 16 +- frame/nfts/src/lib.rs | 57 ++++--- frame/nfts/src/tests.rs | 155 +++++++++++++----- frame/nfts/src/types.rs | 61 ++++++- .../support/src/traits/tokens/nonfungible.rs | 29 +++- .../support/src/traits/tokens/nonfungibles.rs | 3 +- 8 files changed, 274 insertions(+), 87 deletions(-) diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/common.rs index 805f254398c56..b4f7bff165597 100644 --- a/frame/nfts/src/features/common.rs +++ b/frame/nfts/src/features/common.rs @@ -27,6 +27,15 @@ impl, I: 'static> Pallet { Ok(config.values()) } + pub fn get_item_settings( + collection_id: &T::CollectionId, + item_id: &T::ItemId, + ) -> Result { + let config = ItemConfigOf::::get(&collection_id, &item_id) + .ok_or(Error::::UnknownItem)?; + Ok(config.values()) + } + pub fn is_collection_setting_disabled( collection_id: &T::CollectionId, setting: CollectionSetting, @@ -35,6 +44,15 @@ impl, I: 'static> Pallet { Ok((!settings.contains(setting), settings)) } + pub fn is_item_setting_disabled( + collection_id: &T::CollectionId, + item_id: &T::ItemId, + setting: ItemSetting, + ) -> Result<(bool, ItemSettings), DispatchError> { + let settings = Self::get_item_settings(&collection_id, &item_id)?; + Ok((!settings.contains(setting), settings)) + } + pub fn is_feature_flag_set(feature: SystemFeature) -> bool { let features = T::FeatureFlags::get(); return features.0.contains(feature) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index aa029334b8a8a..a3afa50c32321 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -44,9 +44,12 @@ impl, I: 'static> Pallet { )?; ensure!(action_allowed, Error::::ItemsNotTransferable); + let (action_allowed, _) = + Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; + ensure!(action_allowed, Error::::Locked); + let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; - ensure!(!details.is_frozen, Error::::Frozen); with_details(&collection_details, &mut details)?; Account::::remove((&details.owner, &collection, &item)); @@ -140,6 +143,8 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&collection_details.owner, collection_details.total_deposit); CollectionMaxSupply::::remove(&collection); CollectionConfigOf::::remove(&collection); + #[allow(deprecated)] + ItemConfigOf::::remove_prefix(&collection, None); Self::deposit_event(Event::Destroyed { collection }); @@ -155,6 +160,7 @@ impl, I: 'static> Pallet { collection: T::CollectionId, item: T::ItemId, owner: T::AccountId, + config: ItemConfig, with_details: impl FnOnce(&CollectionDetailsFor) -> DispatchResult, ) -> DispatchResult { ensure!(!Item::::contains_key(collection, item), Error::::AlreadyExists); @@ -186,12 +192,9 @@ impl, I: 'static> Pallet { let owner = owner.clone(); Account::::insert((&owner, &collection, &item), ()); - let details = ItemDetails { - owner, - approvals: ApprovalsOf::::default(), - is_frozen: false, - deposit, - }; + ItemConfigOf::::insert(&collection, &item, config); + let details = + ItemDetails { owner, approvals: ApprovalsOf::::default(), deposit }; Item::::insert(&collection, &item, details); Ok(()) }, @@ -226,6 +229,7 @@ impl, I: 'static> Pallet { Item::::remove(&collection, &item); Account::::remove((&owner, &collection, &item)); ItemPriceOf::::remove(&collection, &item); + ItemConfigOf::::remove(&collection, &item); Self::deposit_event(Event::Burned { collection, item, owner }); Ok(()) @@ -247,6 +251,10 @@ impl, I: 'static> Pallet { )?; ensure!(action_allowed, Error::::ItemsNotTransferable); + let (action_allowed, _) = + Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; + ensure!(action_allowed, Error::::Locked); + if let Some(ref price) = price { ItemPriceOf::::insert(&collection, &item, (price, whitelisted_buyer.clone())); Self::deposit_event(Event::ItemPriceSet { diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 7efeacbb07bcc..7c7b6554ae731 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -78,10 +78,13 @@ impl, I: 'static> Inspect<::AccountId> for Palle /// /// Default implementation is that all items are transferable. fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool { - match (CollectionConfigOf::::get(collection), Item::::get(collection, item)) { - (Some(cc), Some(id)) + match ( + CollectionConfigOf::::get(collection), + ItemConfigOf::::get(collection, item), + ) { + (Some(cc), Some(ic)) if !cc.values().contains(CollectionSetting::NonTransferableItems) && - !id.is_frozen => + !ic.values().contains(ItemSetting::NonTransferable) => true, _ => false, } @@ -130,13 +133,16 @@ impl, I: 'static> Destroy<::AccountId> for Palle } } -impl, I: 'static> Mutate<::AccountId> for Pallet { +impl, I: 'static> Mutate<::AccountId, ItemSettings> + for Pallet +{ fn mint_into( collection: &Self::CollectionId, item: &Self::ItemId, who: &T::AccountId, + settings: &ItemSettings, ) -> DispatchResult { - Self::do_mint(*collection, *item, who.clone(), |_| Ok(())) + Self::do_mint(*collection, *item, who.clone(), ItemConfig(*settings), |_| Ok(())) } fn burn( diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 34abf899dcd28..66d41150ca0d8 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -316,10 +316,22 @@ pub mod pallet { pub(super) type NextCollectionId, I: 'static = ()> = StorageValue<_, T::CollectionId, OptionQuery>; - /// Maps a unique collection id to it's config. #[pallet::storage] + /// Config of a collection. pub(super) type CollectionConfigOf, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig>; + StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig, OptionQuery>; + + #[pallet::storage] + /// Config of an item. + pub(super) type ItemConfigOf, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::CollectionId, + Blake2_128Concat, + T::ItemId, + ItemConfig, + OptionQuery, + >; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -642,11 +654,12 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, owner: AccountIdLookupOf, + config: ItemConfig, ) -> DispatchResult { let origin = ensure_signed(origin)?; let owner = T::Lookup::lookup(owner)?; - Self::do_mint(collection, item, owner, |collection_details| { + Self::do_mint(collection, item, owner, config, |collection_details| { ensure!(collection_details.issuer == origin, Error::::NoPermission); Ok(()) }) @@ -811,14 +824,15 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; - let mut details = - Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.freezer == origin, Error::::NoPermission); - details.is_frozen = true; - Item::::insert(&collection, &item, &details); + let mut settings = Self::get_item_settings(&collection, &item)?; + if !settings.contains(ItemSetting::NonTransferable) { + settings.insert(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); Self::deposit_event(Event::::Frozen { collection, item }); Ok(()) @@ -842,14 +856,15 @@ pub mod pallet { ) -> DispatchResult { let origin = ensure_signed(origin)?; - let mut details = - Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.admin == origin, Error::::NoPermission); + ensure!(collection_details.freezer == origin, Error::::NoPermission); - details.is_frozen = false; - Item::::insert(&collection, &item, &details); + let mut settings = Self::get_item_settings(&collection, &item)?; + if settings.contains(ItemSetting::NonTransferable) { + settings.remove(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); Self::deposit_event(Event::::Thawed { collection, item }); Ok(()) @@ -1251,10 +1266,11 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let settings = Self::get_collection_settings(&collection)?; + let collection_settings = Self::get_collection_settings(&collection)?; let maybe_is_frozen = match maybe_item { - None => Some(settings.contains(CollectionSetting::LockedAttributes)), - Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), + None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), + Some(item) => Self::get_item_settings(&collection, &item) + .map(|v| v.contains(ItemSetting::NonTransferable)), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); @@ -1265,7 +1281,9 @@ pub mod pallet { let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !settings.contains(CollectionSetting::FreeHolding) && maybe_check_owner.is_some() { + if !collection_settings.contains(CollectionSetting::FreeHolding) && + maybe_check_owner.is_some() + { deposit = T::DepositPerByte::get() .saturating_mul(((key.len() + value.len()) as u32).into()) .saturating_add(T::AttributeDepositBase::get()); @@ -1318,10 +1336,11 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let settings = Self::get_collection_settings(&collection)?; + let collection_settings = Self::get_collection_settings(&collection)?; let maybe_is_frozen = match maybe_item { - None => Some(settings.contains(CollectionSetting::LockedAttributes)), - Some(item) => ItemMetadataOf::::get(collection, item).map(|v| v.is_frozen), + None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), + Some(item) => Self::get_item_settings(&collection, &item) + .map(|v| v.contains(ItemSetting::NonTransferable)), }; ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 04419c4f82ae7..0da5b1b8ab03d 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -92,6 +92,10 @@ fn default_collection_config() -> CollectionConfig { CollectionConfig(CollectionSetting::FreeHolding.into()) } +fn default_item_config() -> ItemConfig { + ItemConfig::empty() +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -104,12 +108,12 @@ fn basic_minting_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_eq!(collections(), vec![(1, 0)]); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_eq!(items(), vec![(1, 0, 42)]); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 2, default_collection_config())); assert_eq!(collections(), vec![(1, 0), (2, 1)]); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 1, 69, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 1, 69, 1, default_item_config())); assert_eq!(items(), vec![(1, 0, 42), (1, 1, 69)]); }); } @@ -125,9 +129,9 @@ fn lifecycle_should_work() { assert_eq!(Balances::reserved_balance(&1), 5); assert!(CollectionMetadataOf::::contains_key(0)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 10)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 10, default_item_config())); assert_eq!(Balances::reserved_balance(&1), 6); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 20)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 20, default_item_config())); assert_eq!(Balances::reserved_balance(&1), 7); assert_eq!(items(), vec![(10, 0, 42), (20, 0, 69)]); assert_eq!(Collection::::get(0).unwrap().items, 2); @@ -165,7 +169,7 @@ fn destroy_with_bad_witness_should_not_work() { assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); let w = Collection::::get(0).unwrap().destroy_witness(); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_noop!(Nfts::destroy(RuntimeOrigin::signed(1), 0, w), Error::::BadWitness); }); } @@ -174,7 +178,7 @@ fn destroy_with_bad_witness_should_not_work() { fn mint_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_eq!(Nfts::owner(0, 42).unwrap(), 1); assert_eq!(collections(), vec![(1, 0)]); assert_eq!(items(), vec![(1, 0, 42)]); @@ -185,7 +189,7 @@ fn mint_should_work() { fn transfer_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(2), 0, 42, 3)); assert_eq!(items(), vec![(3, 0, 42)]); @@ -207,7 +211,7 @@ fn transfer_should_work() { ) )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, 1, 42)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, 1, 42, default_item_config())); assert_noop!( Nfts::transfer(RuntimeOrigin::signed(1), collection_id, 42, 3,), @@ -220,9 +224,9 @@ fn transfer_should_work() { fn freezing_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_ok!(Nfts::freeze(RuntimeOrigin::signed(1), 0, 42)); - assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Frozen); + assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Locked); assert_ok!(Nfts::thaw(RuntimeOrigin::signed(1), 0, 42)); assert_ok!(Nfts::lock_collection( @@ -252,7 +256,7 @@ fn freezing_should_work() { fn origin_guards_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); Balances::make_free_balance_be(&2, 100); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(2), Some(0))); @@ -266,7 +270,10 @@ fn origin_guards_should_work() { ); assert_noop!(Nfts::freeze(RuntimeOrigin::signed(2), 0, 42), Error::::NoPermission); assert_noop!(Nfts::thaw(RuntimeOrigin::signed(2), 0, 42), Error::::NoPermission); - assert_noop!(Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 2), Error::::NoPermission); + assert_noop!( + Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 2, default_item_config()), + Error::::NoPermission + ); assert_noop!( Nfts::burn(RuntimeOrigin::signed(2), 0, 42, None), Error::::NoPermission @@ -305,7 +312,7 @@ fn transfer_owner_should_work() { // Mint and set metadata now and make sure that deposit gets transferred back. assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20])); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20], false)); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(3), Some(0))); assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(2), 0, 3)); @@ -330,9 +337,9 @@ fn set_team_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2, default_item_config())); assert_ok!(Nfts::freeze(RuntimeOrigin::signed(4), 0, 42)); - assert_ok!(Nfts::thaw(RuntimeOrigin::signed(3), 0, 42)); + assert_ok!(Nfts::thaw(RuntimeOrigin::signed(4), 0, 42)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 3)); assert_ok!(Nfts::burn(RuntimeOrigin::signed(3), 0, 42, None)); }); @@ -416,7 +423,7 @@ fn set_item_metadata_should_work() { // Cannot add metadata to unknown item assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); // Cannot add metadata to unowned item assert_noop!( Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20], false), @@ -554,12 +561,12 @@ fn set_attribute_should_respect_freeze() { assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1])); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 0, bvec![], true)); - let e = Error::::Frozen; + /*let e = Error::::Frozen; assert_noop!( Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1]), e ); - assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(1), bvec![0], bvec![1])); + assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(1), bvec![0], bvec![1]));*/ }); } @@ -569,8 +576,8 @@ fn force_collection_status_should_work() { Balances::make_free_balance_be(&1, 100); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20], false)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20], false)); @@ -586,8 +593,8 @@ fn force_collection_status_should_work() { 1, CollectionConfig(CollectionSetting::FreeHolding.into()), )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2, default_item_config())); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 142, bvec![0; 20], false)); assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 169, bvec![0; 20], false)); assert_eq!(Balances::reserved_balance(1), 65); @@ -618,8 +625,8 @@ fn burn_works() { Error::::UnknownCollection ); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 5)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 5)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 5, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 5, default_item_config())); assert_eq!(Balances::reserved_balance(1), 2); assert_noop!( @@ -641,7 +648,7 @@ fn burn_works() { fn approval_lifecycle_works() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 4)); assert_noop!( @@ -663,7 +670,13 @@ fn approval_lifecycle_works() { ) )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 1, collection_id, 1)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(1), + 1, + collection_id, + 1, + default_item_config() + )); assert_noop!( Nfts::approve_transfer(RuntimeOrigin::signed(1), collection_id, 1, 2, None), @@ -676,7 +689,7 @@ fn approval_lifecycle_works() { fn cancel_approval_works() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_noop!( @@ -704,7 +717,7 @@ fn cancel_approval_works() { let current_block = 1; System::set_block_number(current_block); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); // approval expires after 2 blocks. assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, Some(2))); assert_noop!( @@ -723,7 +736,7 @@ fn cancel_approval_works() { fn approving_multiple_accounts_works() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); let current_block = 1; System::set_block_number(current_block); @@ -748,7 +761,7 @@ fn approving_multiple_accounts_works() { fn approvals_limit_works() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); for i in 3..13 { assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, i, None)); @@ -772,7 +785,7 @@ fn approval_deadline_works() { 1, CollectionConfig(CollectionSetting::FreeHolding.into()) )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); // the approval expires after the 2nd block. assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, Some(2))); @@ -799,7 +812,7 @@ fn approval_deadline_works() { fn cancel_approval_works_with_admin() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_noop!( @@ -827,7 +840,7 @@ fn cancel_approval_works_with_admin() { fn cancel_approval_works_with_force() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_noop!( @@ -855,7 +868,7 @@ fn cancel_approval_works_with_force() { fn clear_all_transfer_approvals_works() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 3, None)); assert_ok!(Nfts::approve_transfer(RuntimeOrigin::signed(2), 0, 42, 4, None)); @@ -918,10 +931,28 @@ fn max_supply_should_work() { ); // validate we can't mint more to max supply - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 0, user_id)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 1, user_id)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + 0, + user_id, + default_item_config() + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + 1, + user_id, + default_item_config() + )); assert_noop!( - Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, 2, user_id), + Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + 2, + user_id, + default_item_config() + ), Error::::MaxSupplyReached ); @@ -945,8 +976,20 @@ fn set_price_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_2, user_id)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_1, + user_id, + default_item_config() + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_2, + user_id, + default_item_config() + )); assert_ok!(Nfts::set_price( RuntimeOrigin::signed(user_id), @@ -1003,7 +1046,13 @@ fn set_price_should_work() { ) )); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_1, + user_id, + default_item_config() + )); assert_noop!( Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_1, Some(2), None), @@ -1032,9 +1081,27 @@ fn buy_item_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_3, user_1)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_1, + user_1, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_2, + user_1, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_3, + user_1, + default_item_config(), + )); assert_ok!(Nfts::set_price( RuntimeOrigin::signed(user_1), @@ -1152,7 +1219,7 @@ fn buy_item_should_work() { }); assert_noop!( buy_item_call.dispatch(RuntimeOrigin::signed(user_2)), - Error::::Frozen + Error::::Locked ); } }); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index c2517adbc7f30..a3c147760a6e8 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -94,8 +94,6 @@ pub struct ItemDetails { pub(super) owner: AccountId, /// The approved transferrer of this item, if one is set. pub(super) approvals: Approvals, - /// Whether the item can be transferred or not. - pub(super) is_frozen: bool, /// The amount held in the pallet's default account for this item. Free-hold items will have /// this as zero. pub(super) deposit: DepositBalance, @@ -204,6 +202,65 @@ impl TypeInfo for CollectionConfig { } } +// Support for up to 64 user-enabled features on an item. +#[bitflags] +#[repr(u64)] +#[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub enum ItemSetting { + /// Disallow transferring this item. + NonTransferable, + /// Disallow to modify metadata of this item. + LockedMetadata, + /// Disallow to modify attributes of this item. + LockedAttributes, +} + +pub(super) type ItemSettings = BitFlags; + +/// Wrapper type for `ItemSettings` that implements `Codec`. +#[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] +pub struct ItemConfig(pub ItemSettings); + +impl ItemConfig { + pub fn empty() -> Self { + Self(BitFlags::EMPTY) + } + + pub fn values(&self) -> ItemSettings { + self.0 + } +} + +impl MaxEncodedLen for ItemConfig { + fn max_encoded_len() -> usize { + u64::max_encoded_len() + } +} + +impl Encode for ItemConfig { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } +} +impl EncodeLike for ItemConfig {} +impl Decode for ItemConfig { + fn decode(input: &mut I) -> sp_std::result::Result { + let field = u64::decode(input)?; + Ok(Self(ItemSettings::from_bits(field as u64).map_err(|_| "invalid value")?)) + } +} + +impl TypeInfo for ItemConfig { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) + .composite(Fields::unnamed().field(|f| f.ty::().type_name("ItemSetting"))) + } +} + // Support for up to 64 system-enabled features on a collection. #[bitflags] #[repr(u64)] diff --git a/frame/support/src/traits/tokens/nonfungible.rs b/frame/support/src/traits/tokens/nonfungible.rs index fe0d2e729930e..f46979a9abad9 100644 --- a/frame/support/src/traits/tokens/nonfungible.rs +++ b/frame/support/src/traits/tokens/nonfungible.rs @@ -74,11 +74,11 @@ pub trait InspectEnumerable: Inspect { /// Trait for providing an interface for NFT-like items which may be minted, burned and/or have /// attributes set on them. -pub trait Mutate: Inspect { +pub trait Mutate: Inspect { /// Mint some `item` to be owned by `who`. /// /// By default, this is not a supported operation. - fn mint_into(_item: &Self::ItemId, _who: &AccountId) -> DispatchResult { + fn mint_into(_item: &Self::ItemId, _who: &AccountId, _config: &ItemConfig) -> DispatchResult { Err(TokenError::Unsupported.into()) } @@ -158,26 +158,37 @@ impl< } impl< - F: nonfungibles::Mutate, + F: nonfungibles::Mutate, A: Get<>::CollectionId>, AccountId, - > Mutate for ItemOf + ItemConfig, + > Mutate for ItemOf { - fn mint_into(item: &Self::ItemId, who: &AccountId) -> DispatchResult { - >::mint_into(&A::get(), item, who) + fn mint_into(item: &Self::ItemId, who: &AccountId, config: &ItemConfig) -> DispatchResult { + >::mint_into(&A::get(), item, who, config) } fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult { - >::burn(&A::get(), item, maybe_check_owner) + >::burn(&A::get(), item, maybe_check_owner) } fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult { - >::set_attribute(&A::get(), item, key, value) + >::set_attribute( + &A::get(), + item, + key, + value, + ) } fn set_typed_attribute( item: &Self::ItemId, key: &K, value: &V, ) -> DispatchResult { - >::set_typed_attribute(&A::get(), item, key, value) + >::set_typed_attribute( + &A::get(), + item, + key, + value, + ) } } diff --git a/frame/support/src/traits/tokens/nonfungibles.rs b/frame/support/src/traits/tokens/nonfungibles.rs index edad84e7a2179..d23e6d67573c7 100644 --- a/frame/support/src/traits/tokens/nonfungibles.rs +++ b/frame/support/src/traits/tokens/nonfungibles.rs @@ -159,7 +159,7 @@ pub trait Destroy: Inspect { /// Trait for providing an interface for multiple collections of NFT-like items which may be /// minted, burned and/or have attributes set on them. -pub trait Mutate: Inspect { +pub trait Mutate: Inspect { /// Mint some `item` of `collection` to be owned by `who`. /// /// By default, this is not a supported operation. @@ -167,6 +167,7 @@ pub trait Mutate: Inspect { _collection: &Self::CollectionId, _item: &Self::ItemId, _who: &AccountId, + _config: &ItemConfig, ) -> DispatchResult { Err(TokenError::Unsupported.into()) } From 08a20480fcfe8c08979abaf66041336ceffa19bb Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 14:47:50 +0300 Subject: [PATCH 26/63] Allow to lock the metadata or attributes of an item --- frame/nfts/src/benchmarking.rs | 4 +- frame/nfts/src/lib.rs | 102 ++++++++++++++++++++++++++------- frame/nfts/src/tests.rs | 67 ++++++++++------------ frame/nfts/src/types.rs | 2 - 4 files changed, 114 insertions(+), 61 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 8ce967b476dec..dbb257b74c5ad 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -322,9 +322,9 @@ benchmarks_instance_pallet! { let (collection, caller, _) = create_collection::(); let (item, ..) = mint_item::(0); - }: _(SystemOrigin::Signed(caller), collection, item, data.clone(), false) + }: _(SystemOrigin::Signed(caller), collection, item, data.clone()) verify { - assert_last_event::(Event::MetadataSet { collection, item, data, is_frozen: false }.into()); + assert_last_event::(Event::MetadataSet { collection, item, data }.into()); } clear_metadata { diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 66d41150ca0d8..cff0500e84ecc 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -357,7 +357,14 @@ pub mod pallet { Frozen { collection: T::CollectionId, item: T::ItemId }, /// Some `item` was thawed. Thawed { collection: T::CollectionId, item: T::ItemId }, - /// Some `collection` was frozen. + /// Some `item` was locked. + ItemLocked { + collection: T::CollectionId, + item: T::ItemId, + lock_metadata: bool, + lock_attributes: bool, + }, + /// Some `collection` was locked. CollectionLocked { collection: T::CollectionId }, /// The owner changed. OwnerChanged { collection: T::CollectionId, new_owner: T::AccountId }, @@ -398,7 +405,6 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, data: BoundedVec, - is_frozen: bool, }, /// Metadata has been cleared for an item. MetadataCleared { collection: T::CollectionId, item: T::ItemId }, @@ -1226,6 +1232,61 @@ pub mod pallet { }) } + /// Disallows changing the metadata of attributes of the item. + /// + /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the + /// `collection`. + /// + /// - `collection`: The collection if the `item`. + /// - `item`: An item to be locked. + /// - `lock_config`: The config with the settings to be locked. + /// + /// Note: when the metadata or attributes are locked, it won't be possible the unlock them. + /// Emits `ItemLocked`. + /// + /// Weight: `O(1)` + #[pallet::weight(0)] + pub fn lock_item( + origin: OriginFor, + collection: T::CollectionId, + item: T::ItemId, + lock_metadata: bool, + lock_attributes: bool, + ) -> DispatchResult { + let maybe_check_owner = T::ForceOrigin::try_origin(origin) + .map(|_| None) + .or_else(|origin| ensure_signed(origin).map(Some))?; + + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &collection_details.owner, Error::::NoPermission); + } + + ItemConfigOf::::try_mutate(collection, item, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; + let mut settings = config.values(); + + if lock_metadata { + settings.insert(ItemSetting::LockedMetadata); + } + if lock_attributes { + settings.insert(ItemSetting::LockedAttributes); + } + + config.0 = settings; + + Self::deposit_event(Event::::ItemLocked { + collection, + item, + lock_metadata, + lock_attributes, + }); + Ok(()) + }) + } + /// Set an attribute for a collection or item. /// /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the @@ -1270,9 +1331,9 @@ pub mod pallet { let maybe_is_frozen = match maybe_item { None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), Some(item) => Self::get_item_settings(&collection, &item) - .map(|v| v.contains(ItemSetting::NonTransferable)), - }; - ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); + .map(|v| v.contains(ItemSetting::LockedAttributes)), + }?; + ensure!(!maybe_is_frozen, Error::::Frozen); let attribute = Attribute::::get((collection, maybe_item, &key)); if attribute.is_none() { @@ -1340,9 +1401,9 @@ pub mod pallet { let maybe_is_frozen = match maybe_item { None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), Some(item) => Self::get_item_settings(&collection, &item) - .map(|v| v.contains(ItemSetting::NonTransferable)), - }; - ensure!(!maybe_is_frozen.unwrap_or(false), Error::::Frozen); + .map(|v| v.contains(ItemSetting::LockedAttributes)), + }?; + ensure!(!maybe_is_frozen, Error::::Frozen); if let Some((_, deposit)) = Attribute::::take((collection, maybe_item, &key)) { collection_details.attributes.saturating_dec(); @@ -1366,7 +1427,6 @@ pub mod pallet { /// - `collection`: The identifier of the collection whose item's metadata to set. /// - `item`: The identifier of the item whose metadata to set. /// - `data`: The general information of this item. Limited in length by `StringLimit`. - /// - `is_frozen`: Whether the metadata should be frozen against further changes. /// /// Emits `MetadataSet`. /// @@ -1377,7 +1437,6 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, data: BoundedVec, - is_frozen: bool, ) -> DispatchResult { let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) @@ -1386,23 +1445,25 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - let settings = Self::get_collection_settings(&collection)?; + let (action_allowed, _) = + Self::is_item_setting_disabled(&collection, &item, ItemSetting::LockedMetadata)?; + ensure!(maybe_check_owner.is_none() || action_allowed, Error::::Frozen); + + let collection_settings = Self::get_collection_settings(&collection)?; if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { - let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); - ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); - if metadata.is_none() { collection_details.item_metadatas.saturating_inc(); } let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !settings.contains(CollectionSetting::FreeHolding) && maybe_check_owner.is_some() + if !collection_settings.contains(CollectionSetting::FreeHolding) && + maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) @@ -1415,10 +1476,10 @@ pub mod pallet { } collection_details.total_deposit.saturating_accrue(deposit); - *metadata = Some(ItemMetadata { deposit, data: data.clone(), is_frozen }); + *metadata = Some(ItemMetadata { deposit, data: data.clone() }); Collection::::insert(&collection, &collection_details); - Self::deposit_event(Event::MetadataSet { collection, item, data, is_frozen }); + Self::deposit_event(Event::MetadataSet { collection, item, data }); Ok(()) }) } @@ -1452,10 +1513,11 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { - let was_frozen = metadata.as_ref().map_or(false, |m| m.is_frozen); - ensure!(maybe_check_owner.is_none() || !was_frozen, Error::::Frozen); + let (action_allowed, _) = + Self::is_item_setting_disabled(&collection, &item, ItemSetting::LockedMetadata)?; + ensure!(maybe_check_owner.is_none() || action_allowed, Error::::Frozen); + ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { if metadata.is_some() { collection_details.item_metadatas.saturating_dec(); } diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 0da5b1b8ab03d..8f596cf78c9b8 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -137,10 +137,10 @@ fn lifecycle_should_work() { assert_eq!(Collection::::get(0).unwrap().items, 2); assert_eq!(Collection::::get(0).unwrap().item_metadatas, 0); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![42, 42], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![42, 42])); assert_eq!(Balances::reserved_balance(&1), 10); assert!(ItemMetadataOf::::contains_key(0, 42)); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![69, 69], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![69, 69])); assert_eq!(Balances::reserved_balance(&1), 13); assert!(ItemMetadataOf::::contains_key(0, 69)); @@ -313,7 +313,7 @@ fn transfer_owner_should_work() { // Mint and set metadata now and make sure that deposit gets transferred back. assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20])); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20])); assert_ok!(Nfts::set_accept_ownership(RuntimeOrigin::signed(3), Some(0))); assert_ok!(Nfts::transfer_ownership(RuntimeOrigin::signed(2), 0, 3)); assert_eq!(collections(), vec![(3, 0)]); @@ -426,40 +426,41 @@ fn set_item_metadata_should_work() { assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); // Cannot add metadata to unowned item assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20], false), + Nfts::set_metadata(RuntimeOrigin::signed(2), 0, 42, bvec![0u8; 20]), Error::::NoPermission, ); // Successfully add metadata and take deposit - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 20])); assert_eq!(Balances::free_balance(&1), 8); assert!(ItemMetadataOf::::contains_key(0, 42)); // Force origin works, too. - assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 18], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 18])); // Update deposit - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15])); assert_eq!(Balances::free_balance(&1), 13); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 25], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 25])); assert_eq!(Balances::free_balance(&1), 3); // Cannot over-reserve assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 40], false), + Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 40]), BalancesError::::InsufficientBalance, ); // Can't set or clear metadata once frozen - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15], true)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15])); + assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 42, true, false)); assert_noop!( - Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15], false), + Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15]), Error::::Frozen, ); assert_noop!(Nfts::clear_metadata(RuntimeOrigin::signed(1), 0, 42), Error::::Frozen); // Clear Metadata - assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 15], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 15])); assert_noop!( Nfts::clear_metadata(RuntimeOrigin::signed(2), 0, 42), Error::::NoPermission @@ -468,19 +469,8 @@ fn set_item_metadata_should_work() { Nfts::clear_metadata(RuntimeOrigin::signed(1), 1, 42), Error::::UnknownCollection ); - assert_ok!(Nfts::clear_metadata(RuntimeOrigin::signed(1), 0, 42)); + assert_ok!(Nfts::clear_metadata(RuntimeOrigin::root(), 0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 42)); - - // collection's metadata can't be changed after the collection gets locked - /*assert_ok!(Nfts::change_collection_config( - Origin::signed(1), - 0, - CollectionConfig(CollectionSetting::LockedMetadata.into()) - )); - assert_noop!( - Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20], false), - Error::::CollectionIsLocked - );*/ }); } @@ -490,6 +480,7 @@ fn set_attribute_should_work() { Balances::make_free_balance_be(&1, 100); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -502,7 +493,7 @@ fn set_attribute_should_work() { (Some(0), bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 9); + assert_eq!(Balances::reserved_balance(1), 10); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0; 10])); assert_eq!( @@ -513,14 +504,14 @@ fn set_attribute_should_work() { (Some(0), bvec![1], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 18); + assert_eq!(Balances::reserved_balance(1), 19); assert_ok!(Nfts::clear_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![1])); assert_eq!( attributes(0), vec![(None, bvec![0], bvec![0; 10]), (Some(0), bvec![0], bvec![0]),] ); - assert_eq!(Balances::reserved_balance(1), 15); + assert_eq!(Balances::reserved_balance(1), 16); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::destroy(RuntimeOrigin::signed(1), 0, w)); @@ -535,6 +526,8 @@ fn set_attribute_should_respect_freeze() { Balances::make_free_balance_be(&1, 100); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, default_item_config())); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0])); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![0])); @@ -547,7 +540,7 @@ fn set_attribute_should_respect_freeze() { (Some(1), bvec![0], bvec![0]), ] ); - assert_eq!(Balances::reserved_balance(1), 9); + assert_eq!(Balances::reserved_balance(1), 11); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![])); assert_ok!(Nfts::lock_collection( @@ -560,13 +553,13 @@ fn set_attribute_should_respect_freeze() { assert_noop!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0]), e); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1])); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 0, bvec![], true)); - /*let e = Error::::Frozen; + assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 0, false, true)); + let e = Error::::Frozen; assert_noop!( Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1]), e ); - assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(1), bvec![0], bvec![1]));*/ + assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(1), bvec![0], bvec![1])); }); } @@ -579,8 +572,8 @@ fn force_collection_status_should_work() { assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20], false)); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20])); assert_eq!(Balances::reserved_balance(1), 65); // force item status to be free holding @@ -595,17 +588,17 @@ fn force_collection_status_should_work() { )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2, default_item_config())); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 142, bvec![0; 20], false)); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 169, bvec![0; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 142, bvec![0; 20])); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 169, bvec![0; 20])); assert_eq!(Balances::reserved_balance(1), 65); assert_ok!(Nfts::redeposit(RuntimeOrigin::signed(1), 0, bvec![0, 42, 50, 69, 100])); assert_eq!(Balances::reserved_balance(1), 63); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0; 20])); assert_eq!(Balances::reserved_balance(1), 42); - assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20], false)); + assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 69, bvec![0; 20])); assert_eq!(Balances::reserved_balance(1), 21); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index a3c147760a6e8..a5b49c653be96 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -125,8 +125,6 @@ pub struct ItemMetadata> { /// generally be either a JSON dump or the hash of some JSON which can be found on a /// hash-addressable global publication system such as IPFS. pub(super) data: BoundedVec, - /// Whether the item metadata may be changed by a non Force origin. - pub(super) is_frozen: bool, } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] From 155171408a7a411b7bc34cf6bb5b949de71851dc Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 14:48:53 +0300 Subject: [PATCH 27/63] Common -> Settings --- frame/nfts/src/features/mod.rs | 2 +- frame/nfts/src/features/{common.rs => settings.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename frame/nfts/src/features/{common.rs => settings.rs} (100%) diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index f4a455d772c94..9d16476251524 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -16,4 +16,4 @@ // limitations under the License. pub mod buy_sell; -pub mod common; +pub mod settings; diff --git a/frame/nfts/src/features/common.rs b/frame/nfts/src/features/settings.rs similarity index 100% rename from frame/nfts/src/features/common.rs rename to frame/nfts/src/features/settings.rs From 8830a4c3c8f9729252e5de769f4d3ba20b354c5a Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 27 Sep 2022 15:04:35 +0300 Subject: [PATCH 28/63] Extract settings related code to a separate file --- frame/nfts/src/features/settings.rs | 78 +++++++++++++++++++++++++++++ frame/nfts/src/lib.rs | 61 ++-------------------- 2 files changed, 82 insertions(+), 57 deletions(-) diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index b4f7bff165597..d43d8bfebbfe6 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -19,6 +19,84 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { + pub fn do_lock_collection( + collection: T::CollectionId, + lock_config: CollectionConfig, + ) -> DispatchResult { + CollectionConfigOf::::try_mutate(collection, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; + let mut settings = config.values(); + let lock_settings = lock_config.values(); + + if lock_settings.contains(CollectionSetting::NonTransferableItems) { + settings.insert(CollectionSetting::NonTransferableItems); + } + if lock_settings.contains(CollectionSetting::LockedMetadata) { + settings.insert(CollectionSetting::LockedMetadata); + } + if lock_settings.contains(CollectionSetting::LockedAttributes) { + settings.insert(CollectionSetting::LockedAttributes); + } + + config.0 = settings; + + Self::deposit_event(Event::::CollectionLocked { collection }); + Ok(()) + }) + } + + pub fn do_lock_item( + collection: T::CollectionId, + item: T::ItemId, + lock_metadata: bool, + lock_attributes: bool, + ) -> DispatchResult { + ItemConfigOf::::try_mutate(collection, item, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; + let mut settings = config.values(); + + if lock_metadata { + settings.insert(ItemSetting::LockedMetadata); + } + if lock_attributes { + settings.insert(ItemSetting::LockedAttributes); + } + + config.0 = settings; + + Self::deposit_event(Event::::ItemLocked { + collection, + item, + lock_metadata, + lock_attributes, + }); + Ok(()) + }) + } + + pub fn do_freeze_item(collection: T::CollectionId, item: T::ItemId) -> DispatchResult { + let mut settings = Self::get_item_settings(&collection, &item)?; + if !settings.contains(ItemSetting::NonTransferable) { + settings.insert(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::Frozen { collection, item }); + Ok(()) + } + + pub fn do_thaw_item(collection: T::CollectionId, item: T::ItemId) -> DispatchResult { + let mut settings = Self::get_item_settings(&collection, &item)?; + if settings.contains(ItemSetting::NonTransferable) { + settings.remove(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::Thawed { collection, item }); + Ok(()) + } + + // helpers pub fn get_collection_settings( collection_id: &T::CollectionId, ) -> Result { diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index cff0500e84ecc..3c3e4c00b6278 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -834,14 +834,7 @@ pub mod pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.freezer == origin, Error::::NoPermission); - let mut settings = Self::get_item_settings(&collection, &item)?; - if !settings.contains(ItemSetting::NonTransferable) { - settings.insert(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Frozen { collection, item }); - Ok(()) + Self::do_freeze_item(collection, item) } /// Re-allow unprivileged transfer of an item. @@ -866,14 +859,7 @@ pub mod pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.freezer == origin, Error::::NoPermission); - let mut settings = Self::get_item_settings(&collection, &item)?; - if settings.contains(ItemSetting::NonTransferable) { - settings.remove(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Thawed { collection, item }); - Ok(()) + Self::do_thaw_item(collection, item) } /// Disallows specified settings for the whole collection. @@ -899,26 +885,7 @@ pub mod pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(origin == details.freezer, Error::::NoPermission); - CollectionConfigOf::::try_mutate(collection, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; - let mut settings = config.values(); - let lock_settings = lock_config.values(); - - if lock_settings.contains(CollectionSetting::NonTransferableItems) { - settings.insert(CollectionSetting::NonTransferableItems); - } - if lock_settings.contains(CollectionSetting::LockedMetadata) { - settings.insert(CollectionSetting::LockedMetadata); - } - if lock_settings.contains(CollectionSetting::LockedAttributes) { - settings.insert(CollectionSetting::LockedAttributes); - } - - config.0 = settings; - - Self::deposit_event(Event::::CollectionLocked { collection }); - Ok(()) - }) + Self::do_lock_collection(collection, lock_config) } /// Change the Owner of a collection. @@ -1264,27 +1231,7 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - ItemConfigOf::::try_mutate(collection, item, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; - let mut settings = config.values(); - - if lock_metadata { - settings.insert(ItemSetting::LockedMetadata); - } - if lock_attributes { - settings.insert(ItemSetting::LockedAttributes); - } - - config.0 = settings; - - Self::deposit_event(Event::::ItemLocked { - collection, - item, - lock_metadata, - lock_attributes, - }); - Ok(()) - }) + Self::do_lock_item(collection, item, lock_metadata, lock_attributes) } /// Set an attribute for a collection or item. From f8800dffedff40145bd0b5c003b8e3cd4a9cbb22 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 28 Sep 2022 10:29:29 +0300 Subject: [PATCH 29/63] Move feature flag checks inside the do_* methods --- frame/nfts/src/features/settings.rs | 33 +++++++++++++++++++++++-- frame/nfts/src/functions.rs | 10 ++++++++ frame/nfts/src/lib.rs | 38 +++-------------------------- 3 files changed, 45 insertions(+), 36 deletions(-) diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index d43d8bfebbfe6..6385f339ab1b5 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -20,9 +20,14 @@ use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { pub fn do_lock_collection( + origin: T::AccountId, collection: T::CollectionId, lock_config: CollectionConfig, ) -> DispatchResult { + let details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(origin == details.freezer, Error::::NoPermission); + CollectionConfigOf::::try_mutate(collection, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; let mut settings = config.values(); @@ -46,11 +51,19 @@ impl, I: 'static> Pallet { } pub fn do_lock_item( + maybe_check_owner: Option, collection: T::CollectionId, item: T::ItemId, lock_metadata: bool, lock_attributes: bool, ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &collection_details.owner, Error::::NoPermission); + } + ItemConfigOf::::try_mutate(collection, item, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; let mut settings = config.values(); @@ -74,7 +87,15 @@ impl, I: 'static> Pallet { }) } - pub fn do_freeze_item(collection: T::CollectionId, item: T::ItemId) -> DispatchResult { + pub fn do_freeze_item( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + let mut settings = Self::get_item_settings(&collection, &item)?; if !settings.contains(ItemSetting::NonTransferable) { settings.insert(ItemSetting::NonTransferable); @@ -85,7 +106,15 @@ impl, I: 'static> Pallet { Ok(()) } - pub fn do_thaw_item(collection: T::CollectionId, item: T::ItemId) -> DispatchResult { + pub fn do_thaw_item( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + let mut settings = Self::get_item_settings(&collection, &item)?; if settings.contains(ItemSetting::NonTransferable) { settings.remove(ItemSetting::NonTransferable); diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index a3afa50c32321..e952dc04cd68a 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -242,6 +242,11 @@ impl, I: 'static> Pallet { price: Option>, whitelisted_buyer: Option, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoTrading), + Error::::MethodDisabled + ); + let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner == sender, Error::::NoPermission); @@ -277,6 +282,11 @@ impl, I: 'static> Pallet { buyer: T::AccountId, bid_price: ItemPrice, ) -> DispatchResult { + ensure!( + !Self::is_feature_flag_set(SystemFeature::NoTrading), + Error::::MethodDisabled + ); + let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner != buyer, Error::::NoPermission); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 3c3e4c00b6278..d72d200b29588 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -829,12 +829,7 @@ pub mod pallet { item: T::ItemId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - Self::do_freeze_item(collection, item) + Self::do_freeze_item(origin, collection, item) } /// Re-allow unprivileged transfer of an item. @@ -854,12 +849,7 @@ pub mod pallet { item: T::ItemId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - Self::do_thaw_item(collection, item) + Self::do_thaw_item(origin, collection, item) } /// Disallows specified settings for the whole collection. @@ -880,12 +870,7 @@ pub mod pallet { lock_config: CollectionConfig, ) -> DispatchResult { let origin = ensure_signed(origin)?; - - let details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(origin == details.freezer, Error::::NoPermission); - - Self::do_lock_collection(collection, lock_config) + Self::do_lock_collection(origin, collection, lock_config) } /// Change the Owner of a collection. @@ -1224,14 +1209,7 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &collection_details.owner, Error::::NoPermission); - } - - Self::do_lock_item(collection, item, lock_metadata, lock_attributes) + Self::do_lock_item(maybe_check_owner, collection, item, lock_metadata, lock_attributes) } /// Set an attribute for a collection or item. @@ -1682,10 +1660,6 @@ pub mod pallet { whitelisted_buyer: Option>, ) -> DispatchResult { let origin = ensure_signed(origin)?; - ensure!( - !Self::is_feature_flag_set(SystemFeature::NoTrading), - Error::::MethodDisabled - ); let whitelisted_buyer = whitelisted_buyer.map(T::Lookup::lookup).transpose()?; Self::do_set_price(collection, item, origin, price, whitelisted_buyer) } @@ -1708,10 +1682,6 @@ pub mod pallet { bid_price: ItemPrice, ) -> DispatchResult { let origin = ensure_signed(origin)?; - ensure!( - !Self::is_feature_flag_set(SystemFeature::NoTrading), - Error::::MethodDisabled - ); Self::do_buy_item(collection, item, origin, bid_price) } From 3ccf04ef5f7205839122deb7901508f3024c9f4e Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 28 Sep 2022 10:32:03 +0300 Subject: [PATCH 30/63] Split settings.rs into parts --- frame/nfts/src/features/freeze.rs | 59 +++++++++++++++ frame/nfts/src/features/lock.rs | 89 +++++++++++++++++++++++ frame/nfts/src/features/mod.rs | 2 + frame/nfts/src/features/settings.rs | 107 ---------------------------- 4 files changed, 150 insertions(+), 107 deletions(-) create mode 100644 frame/nfts/src/features/freeze.rs create mode 100644 frame/nfts/src/features/lock.rs diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs new file mode 100644 index 0000000000000..6884911321ab5 --- /dev/null +++ b/frame/nfts/src/features/freeze.rs @@ -0,0 +1,59 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::pallet_prelude::*; + +impl, I: 'static> Pallet { + pub fn do_freeze_item( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + + let mut settings = Self::get_item_settings(&collection, &item)?; + if !settings.contains(ItemSetting::NonTransferable) { + settings.insert(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::Frozen { collection, item }); + Ok(()) + } + + pub fn do_thaw_item( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + + let mut settings = Self::get_item_settings(&collection, &item)?; + if settings.contains(ItemSetting::NonTransferable) { + settings.remove(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::Thawed { collection, item }); + Ok(()) + } +} diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs new file mode 100644 index 0000000000000..2721d64ef11c3 --- /dev/null +++ b/frame/nfts/src/features/lock.rs @@ -0,0 +1,89 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::*; +use frame_support::pallet_prelude::*; + +impl, I: 'static> Pallet { + pub fn do_lock_collection( + origin: T::AccountId, + collection: T::CollectionId, + lock_config: CollectionConfig, + ) -> DispatchResult { + let details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(origin == details.freezer, Error::::NoPermission); + + CollectionConfigOf::::try_mutate(collection, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; + let mut settings = config.values(); + let lock_settings = lock_config.values(); + + if lock_settings.contains(CollectionSetting::NonTransferableItems) { + settings.insert(CollectionSetting::NonTransferableItems); + } + if lock_settings.contains(CollectionSetting::LockedMetadata) { + settings.insert(CollectionSetting::LockedMetadata); + } + if lock_settings.contains(CollectionSetting::LockedAttributes) { + settings.insert(CollectionSetting::LockedAttributes); + } + + config.0 = settings; + + Self::deposit_event(Event::::CollectionLocked { collection }); + Ok(()) + }) + } + + pub fn do_lock_item( + maybe_check_owner: Option, + collection: T::CollectionId, + item: T::ItemId, + lock_metadata: bool, + lock_attributes: bool, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + + if let Some(check_owner) = &maybe_check_owner { + ensure!(check_owner == &collection_details.owner, Error::::NoPermission); + } + + ItemConfigOf::::try_mutate(collection, item, |maybe_config| { + let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; + let mut settings = config.values(); + + if lock_metadata { + settings.insert(ItemSetting::LockedMetadata); + } + if lock_attributes { + settings.insert(ItemSetting::LockedAttributes); + } + + config.0 = settings; + + Self::deposit_event(Event::::ItemLocked { + collection, + item, + lock_metadata, + lock_attributes, + }); + Ok(()) + }) + } +} diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index 9d16476251524..11e424f813b2c 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -16,4 +16,6 @@ // limitations under the License. pub mod buy_sell; +pub mod freeze; +pub mod lock; pub mod settings; diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 6385f339ab1b5..b4f7bff165597 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -19,113 +19,6 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { - pub fn do_lock_collection( - origin: T::AccountId, - collection: T::CollectionId, - lock_config: CollectionConfig, - ) -> DispatchResult { - let details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(origin == details.freezer, Error::::NoPermission); - - CollectionConfigOf::::try_mutate(collection, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; - let mut settings = config.values(); - let lock_settings = lock_config.values(); - - if lock_settings.contains(CollectionSetting::NonTransferableItems) { - settings.insert(CollectionSetting::NonTransferableItems); - } - if lock_settings.contains(CollectionSetting::LockedMetadata) { - settings.insert(CollectionSetting::LockedMetadata); - } - if lock_settings.contains(CollectionSetting::LockedAttributes) { - settings.insert(CollectionSetting::LockedAttributes); - } - - config.0 = settings; - - Self::deposit_event(Event::::CollectionLocked { collection }); - Ok(()) - }) - } - - pub fn do_lock_item( - maybe_check_owner: Option, - collection: T::CollectionId, - item: T::ItemId, - lock_metadata: bool, - lock_attributes: bool, - ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - - if let Some(check_owner) = &maybe_check_owner { - ensure!(check_owner == &collection_details.owner, Error::::NoPermission); - } - - ItemConfigOf::::try_mutate(collection, item, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; - let mut settings = config.values(); - - if lock_metadata { - settings.insert(ItemSetting::LockedMetadata); - } - if lock_attributes { - settings.insert(ItemSetting::LockedAttributes); - } - - config.0 = settings; - - Self::deposit_event(Event::::ItemLocked { - collection, - item, - lock_metadata, - lock_attributes, - }); - Ok(()) - }) - } - - pub fn do_freeze_item( - origin: T::AccountId, - collection: T::CollectionId, - item: T::ItemId, - ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - let mut settings = Self::get_item_settings(&collection, &item)?; - if !settings.contains(ItemSetting::NonTransferable) { - settings.insert(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Frozen { collection, item }); - Ok(()) - } - - pub fn do_thaw_item( - origin: T::AccountId, - collection: T::CollectionId, - item: T::ItemId, - ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - let mut settings = Self::get_item_settings(&collection, &item)?; - if settings.contains(ItemSetting::NonTransferable) { - settings.remove(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Thawed { collection, item }); - Ok(()) - } - - // helpers pub fn get_collection_settings( collection_id: &T::CollectionId, ) -> Result { From f83fb734098aa390766f680fe9fb3d63a285e9df Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 28 Sep 2022 11:49:15 +0300 Subject: [PATCH 31/63] Extract repeated code into macro --- frame/nfts/src/types.rs | 137 ++++++++++++---------------------------- 1 file changed, 41 insertions(+), 96 deletions(-) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index a5b49c653be96..56192233be76f 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -139,6 +139,44 @@ pub struct ItemTip { pub(super) amount: Amount, } +macro_rules! impl_codec_bitflags { + ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { + impl MaxEncodedLen for $wrapper { + fn max_encoded_len() -> usize { + <$size>::max_encoded_len() + } + } + impl Encode for $wrapper { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } + } + impl EncodeLike for $wrapper {} + impl Decode for $wrapper { + fn decode( + input: &mut I, + ) -> sp_std::result::Result { + let field = <$size>::decode(input)?; + Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) + } + } + + impl TypeInfo for $wrapper { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::<$bitflag_enum>()))]) + .composite( + Fields::unnamed() + .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), + ) + } + } + }; +} + // Support for up to 64 user-enabled features on a collection. #[bitflags] #[repr(u64)] @@ -153,7 +191,6 @@ pub enum CollectionSetting { /// When is set then no deposit needed to hold items of this collection. FreeHolding, } - pub(super) type CollectionSettings = BitFlags; /// Wrapper type for `CollectionSettings` that implements `Codec`. @@ -164,41 +201,11 @@ impl CollectionConfig { pub fn empty() -> Self { Self(BitFlags::EMPTY) } - pub fn values(&self) -> CollectionSettings { self.0 } } - -impl MaxEncodedLen for CollectionConfig { - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Encode for CollectionConfig { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } -} -impl EncodeLike for CollectionConfig {} -impl Decode for CollectionConfig { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self(CollectionSettings::from_bits(field as u64).map_err(|_| "invalid value")?)) - } -} - -impl TypeInfo for CollectionConfig { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("CollectionSetting"))) - } -} +impl_codec_bitflags!(CollectionConfig, u64, CollectionSetting); // Support for up to 64 user-enabled features on an item. #[bitflags] @@ -212,52 +219,20 @@ pub enum ItemSetting { /// Disallow to modify attributes of this item. LockedAttributes, } - pub(super) type ItemSettings = BitFlags; /// Wrapper type for `ItemSettings` that implements `Codec`. #[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] pub struct ItemConfig(pub ItemSettings); - impl ItemConfig { pub fn empty() -> Self { Self(BitFlags::EMPTY) } - pub fn values(&self) -> ItemSettings { self.0 } } - -impl MaxEncodedLen for ItemConfig { - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Encode for ItemConfig { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } -} -impl EncodeLike for ItemConfig {} -impl Decode for ItemConfig { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self(ItemSettings::from_bits(field as u64).map_err(|_| "invalid value")?)) - } -} - -impl TypeInfo for ItemConfig { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("ItemSetting"))) - } -} +impl_codec_bitflags!(ItemConfig, u64, ItemSetting); // Support for up to 64 system-enabled features on a collection. #[bitflags] @@ -275,39 +250,9 @@ pub enum SystemFeature { /// Disallow public mints. NoPublicMints, } - pub type SystemFeatureFlags = BitFlags; /// Wrapper type for `SystemFeatureFlags` that implements `Codec`. #[derive(Default, RuntimeDebug)] pub struct SystemFeatures(pub SystemFeatureFlags); - -impl MaxEncodedLen for SystemFeatures { - fn max_encoded_len() -> usize { - u64::max_encoded_len() - } -} - -impl Encode for SystemFeatures { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } -} -impl EncodeLike for SystemFeatures {} -impl Decode for SystemFeatures { - fn decode(input: &mut I) -> sp_std::result::Result { - let field = u64::decode(input)?; - Ok(Self(SystemFeatureFlags::from_bits(field as u64).map_err(|_| "invalid value")?)) - } -} - -impl TypeInfo for SystemFeatures { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::()))]) - .composite(Fields::unnamed().field(|f| f.ty::().type_name("SystemFeature"))) - } -} +impl_codec_bitflags!(SystemFeatures, u64, SystemFeature); From df97cce39c123b7f7b43ab82f3d2b6d36317d573 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 28 Sep 2022 11:49:45 +0300 Subject: [PATCH 32/63] Extract macros into their own file --- frame/nfts/src/features/macros.rs | 72 +++++++++++++++++++++++++++++++ frame/nfts/src/features/mod.rs | 1 + frame/nfts/src/lib.rs | 23 ---------- frame/nfts/src/types.rs | 45 +++---------------- 4 files changed, 80 insertions(+), 61 deletions(-) create mode 100644 frame/nfts/src/features/macros.rs diff --git a/frame/nfts/src/features/macros.rs b/frame/nfts/src/features/macros.rs new file mode 100644 index 0000000000000..d006e82bd2bea --- /dev/null +++ b/frame/nfts/src/features/macros.rs @@ -0,0 +1,72 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +macro_rules! impl_incrementable { + ($($type:ty),+) => { + $( + impl Incrementable for $type { + fn increment(&self) -> Self { + self.saturating_add(1) + } + + fn initial_value() -> Self { + 0 + } + } + )+ + }; +} +pub(crate) use impl_incrementable; + +macro_rules! impl_codec_bitflags { + ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { + impl MaxEncodedLen for $wrapper { + fn max_encoded_len() -> usize { + <$size>::max_encoded_len() + } + } + impl Encode for $wrapper { + fn using_encoded R>(&self, f: F) -> R { + self.0.bits().using_encoded(f) + } + } + impl EncodeLike for $wrapper {} + impl Decode for $wrapper { + fn decode( + input: &mut I, + ) -> sp_std::result::Result { + let field = <$size>::decode(input)?; + Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) + } + } + + impl TypeInfo for $wrapper { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("BitFlags", module_path!())) + .type_params(vec![TypeParameter::new("T", Some(meta_type::<$bitflag_enum>()))]) + .composite( + Fields::unnamed() + .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), + ) + } + } + }; +} +pub(crate) use impl_codec_bitflags; diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index 11e424f813b2c..0657968b07673 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -18,4 +18,5 @@ pub mod buy_sell; pub mod freeze; pub mod lock; +pub mod macros; pub mod settings; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index d72d200b29588..f39c53ad9a32d 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -62,29 +62,6 @@ pub use weights::WeightInfo; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -pub trait Incrementable { - fn increment(&self) -> Self; - fn initial_value() -> Self; -} - -macro_rules! impl_incrementable { - ($($type:ty),+) => { - $( - impl Incrementable for $type { - fn increment(&self) -> Self { - self.saturating_add(1) - } - - fn initial_value() -> Self { - 0 - } - } - )+ - }; -} - -impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); - #[frame_support::pallet] pub mod pallet { use super::*; diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 56192233be76f..d3d7c4069c99a 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -18,6 +18,7 @@ //! Various basic types for use in the Nfts pallet. use super::*; +use crate::features::macros::*; use codec::EncodeLike; use enumflags2::{bitflags, BitFlags}; use frame_support::{ @@ -42,6 +43,12 @@ pub(super) type ItemTipOf = ItemTip< BalanceOf, >; +pub trait Incrementable { + fn increment(&self) -> Self; + fn initial_value() -> Self; +} +impl_incrementable!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct CollectionDetails { /// Can change `owner`, `issuer`, `freezer` and `admin` accounts. @@ -139,44 +146,6 @@ pub struct ItemTip { pub(super) amount: Amount, } -macro_rules! impl_codec_bitflags { - ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { - impl MaxEncodedLen for $wrapper { - fn max_encoded_len() -> usize { - <$size>::max_encoded_len() - } - } - impl Encode for $wrapper { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } - } - impl EncodeLike for $wrapper {} - impl Decode for $wrapper { - fn decode( - input: &mut I, - ) -> sp_std::result::Result { - let field = <$size>::decode(input)?; - Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) - } - } - - impl TypeInfo for $wrapper { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new("T", Some(meta_type::<$bitflag_enum>()))]) - .composite( - Fields::unnamed() - .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), - ) - } - } - }; -} - // Support for up to 64 user-enabled features on a collection. #[bitflags] #[repr(u64)] From 3bb6eb5ccb26c70ba1787ab5cea3f890ad1329bf Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 28 Sep 2022 12:03:50 +0300 Subject: [PATCH 33/63] Chore --- frame/nfts/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index d3d7c4069c99a..2774d2a6d1445 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -193,6 +193,7 @@ pub(super) type ItemSettings = BitFlags; /// Wrapper type for `ItemSettings` that implements `Codec`. #[derive(Clone, Copy, PartialEq, Eq, Default, RuntimeDebug)] pub struct ItemConfig(pub ItemSettings); + impl ItemConfig { pub fn empty() -> Self { Self(BitFlags::EMPTY) From 8563a312a8998caec4498b78183ea19ac1335d75 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:30:04 +0300 Subject: [PATCH 34/63] Fix traits --- frame/support/src/traits/tokens.rs | 2 + .../support/src/traits/tokens/nonfungible.rs | 29 +-- .../src/traits/tokens/nonfungible_v2.rs | 204 +++++++++++++++ .../support/src/traits/tokens/nonfungibles.rs | 6 +- .../src/traits/tokens/nonfungibles_v2.rs | 243 ++++++++++++++++++ 5 files changed, 460 insertions(+), 24 deletions(-) create mode 100644 frame/support/src/traits/tokens/nonfungible_v2.rs create mode 100644 frame/support/src/traits/tokens/nonfungibles_v2.rs diff --git a/frame/support/src/traits/tokens.rs b/frame/support/src/traits/tokens.rs index 77eb83adfbfb0..b3b3b4b7d90b1 100644 --- a/frame/support/src/traits/tokens.rs +++ b/frame/support/src/traits/tokens.rs @@ -23,7 +23,9 @@ pub mod fungibles; pub mod imbalance; mod misc; pub mod nonfungible; +pub mod nonfungible_v2; pub mod nonfungibles; +pub mod nonfungibles_v2; pub use imbalance::Imbalance; pub use misc::{ AssetId, Balance, BalanceConversion, BalanceStatus, DepositConsequence, ExistenceRequirement, diff --git a/frame/support/src/traits/tokens/nonfungible.rs b/frame/support/src/traits/tokens/nonfungible.rs index f46979a9abad9..fe0d2e729930e 100644 --- a/frame/support/src/traits/tokens/nonfungible.rs +++ b/frame/support/src/traits/tokens/nonfungible.rs @@ -74,11 +74,11 @@ pub trait InspectEnumerable: Inspect { /// Trait for providing an interface for NFT-like items which may be minted, burned and/or have /// attributes set on them. -pub trait Mutate: Inspect { +pub trait Mutate: Inspect { /// Mint some `item` to be owned by `who`. /// /// By default, this is not a supported operation. - fn mint_into(_item: &Self::ItemId, _who: &AccountId, _config: &ItemConfig) -> DispatchResult { + fn mint_into(_item: &Self::ItemId, _who: &AccountId) -> DispatchResult { Err(TokenError::Unsupported.into()) } @@ -158,37 +158,26 @@ impl< } impl< - F: nonfungibles::Mutate, + F: nonfungibles::Mutate, A: Get<>::CollectionId>, AccountId, - ItemConfig, - > Mutate for ItemOf + > Mutate for ItemOf { - fn mint_into(item: &Self::ItemId, who: &AccountId, config: &ItemConfig) -> DispatchResult { - >::mint_into(&A::get(), item, who, config) + fn mint_into(item: &Self::ItemId, who: &AccountId) -> DispatchResult { + >::mint_into(&A::get(), item, who) } fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult { - >::burn(&A::get(), item, maybe_check_owner) + >::burn(&A::get(), item, maybe_check_owner) } fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult { - >::set_attribute( - &A::get(), - item, - key, - value, - ) + >::set_attribute(&A::get(), item, key, value) } fn set_typed_attribute( item: &Self::ItemId, key: &K, value: &V, ) -> DispatchResult { - >::set_typed_attribute( - &A::get(), - item, - key, - value, - ) + >::set_typed_attribute(&A::get(), item, key, value) } } diff --git a/frame/support/src/traits/tokens/nonfungible_v2.rs b/frame/support/src/traits/tokens/nonfungible_v2.rs new file mode 100644 index 0000000000000..850195852cf72 --- /dev/null +++ b/frame/support/src/traits/tokens/nonfungible_v2.rs @@ -0,0 +1,204 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with a single non-fungible collection of items. +//! +//! This assumes a single level namespace identified by `Inspect::ItemId`, and could +//! reasonably be implemented by pallets which wants to expose a single collection of NFT-like +//! objects. +//! +//! For an NFT API which has dual-level namespacing, the traits in `nonfungibles` are better to +//! use. + +use super::nonfungibles_v2 as nonfungibles; +use crate::{dispatch::DispatchResult, traits::Get}; +use codec::{Decode, Encode}; +use sp_runtime::TokenError; +use sp_std::prelude::*; + +/// Trait for providing an interface to a read-only NFT-like set of items. +pub trait Inspect { + /// Type for identifying an item. + type ItemId; + + /// Returns the owner of `item`, or `None` if the item doesn't exist or has no + /// owner. + fn owner(item: &Self::ItemId) -> Option; + + /// Returns the attribute value of `item` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn attribute(_item: &Self::ItemId, _key: &[u8]) -> Option> { + None + } + + /// Returns the strongly-typed attribute value of `item` corresponding to `key`. + /// + /// By default this just attempts to use `attribute`. + fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { + key.using_encoded(|d| Self::attribute(item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns `true` if the `item` may be transferred. + /// + /// Default implementation is that all items are transferable. + fn can_transfer(_item: &Self::ItemId) -> bool { + true + } +} + +/// Interface for enumerating items in existence or owned by a given account over a collection +/// of NFTs. +pub trait InspectEnumerable: Inspect { + /// Returns an iterator of the items within a `collection` in existence. + fn items() -> Box>; + + /// Returns an iterator of the items of all collections owned by `who`. + fn owned(who: &AccountId) -> Box>; +} + +/// Trait for providing an interface for NFT-like items which may be minted, burned and/or have +/// attributes set on them. +pub trait Mutate: Inspect { + /// Mint some `item` to be owned by `who`. + /// + /// By default, this is not a supported operation. + fn mint_into(_item: &Self::ItemId, _who: &AccountId, _config: &ItemConfig) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Burn some `item`. + /// + /// By default, this is not a supported operation. + fn burn(_item: &Self::ItemId, _maybe_check_owner: Option<&AccountId>) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Set attribute `value` of `item`'s `key`. + /// + /// By default, this is not a supported operation. + fn set_attribute(_item: &Self::ItemId, _key: &[u8], _value: &[u8]) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Attempt to set the strongly-typed attribute `value` of `item`'s `key`. + /// + /// By default this just attempts to use `set_attribute`. + fn set_typed_attribute( + item: &Self::ItemId, + key: &K, + value: &V, + ) -> DispatchResult { + key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(item, k, v))) + } +} + +/// Trait for providing a non-fungible set of items which can only be transferred. +pub trait Transfer: Inspect { + /// Transfer `item` into `destination` account. + fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult; +} + +/// Convert a `fungibles` trait implementation into a `fungible` trait implementation by identifying +/// a single item. +pub struct ItemOf< + F: nonfungibles::Inspect, + A: Get<>::CollectionId>, + AccountId, +>(sp_std::marker::PhantomData<(F, A, AccountId)>); + +impl< + F: nonfungibles::Inspect, + A: Get<>::CollectionId>, + AccountId, + > Inspect for ItemOf +{ + type ItemId = >::ItemId; + fn owner(item: &Self::ItemId) -> Option { + >::owner(&A::get(), item) + } + fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { + >::attribute(&A::get(), item, key) + } + fn typed_attribute(item: &Self::ItemId, key: &K) -> Option { + >::typed_attribute(&A::get(), item, key) + } + fn can_transfer(item: &Self::ItemId) -> bool { + >::can_transfer(&A::get(), item) + } +} + +impl< + F: nonfungibles::InspectEnumerable, + A: Get<>::CollectionId>, + AccountId, + > InspectEnumerable for ItemOf +{ + fn items() -> Box> { + >::items(&A::get()) + } + fn owned(who: &AccountId) -> Box> { + >::owned_in_collection(&A::get(), who) + } +} + +impl< + F: nonfungibles::Mutate, + A: Get<>::CollectionId>, + AccountId, + ItemConfig, + > Mutate for ItemOf +{ + fn mint_into(item: &Self::ItemId, who: &AccountId, config: &ItemConfig) -> DispatchResult { + >::mint_into(&A::get(), item, who, config) + } + fn burn(item: &Self::ItemId, maybe_check_owner: Option<&AccountId>) -> DispatchResult { + >::burn(&A::get(), item, maybe_check_owner) + } + fn set_attribute(item: &Self::ItemId, key: &[u8], value: &[u8]) -> DispatchResult { + >::set_attribute( + &A::get(), + item, + key, + value, + ) + } + fn set_typed_attribute( + item: &Self::ItemId, + key: &K, + value: &V, + ) -> DispatchResult { + >::set_typed_attribute( + &A::get(), + item, + key, + value, + ) + } +} + +impl< + F: nonfungibles::Transfer, + A: Get<>::CollectionId>, + AccountId, + > Transfer for ItemOf +{ + fn transfer(item: &Self::ItemId, destination: &AccountId) -> DispatchResult { + >::transfer(&A::get(), item, destination) + } +} diff --git a/frame/support/src/traits/tokens/nonfungibles.rs b/frame/support/src/traits/tokens/nonfungibles.rs index d23e6d67573c7..d043a87ce7c10 100644 --- a/frame/support/src/traits/tokens/nonfungibles.rs +++ b/frame/support/src/traits/tokens/nonfungibles.rs @@ -122,13 +122,12 @@ pub trait InspectEnumerable: Inspect { } /// Trait for providing the ability to create collections of nonfungible items. -pub trait Create: Inspect { +pub trait Create: Inspect { /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. fn create_collection( collection: &Self::CollectionId, who: &AccountId, admin: &AccountId, - config: &CollectionConfig, ) -> DispatchResult; } @@ -159,7 +158,7 @@ pub trait Destroy: Inspect { /// Trait for providing an interface for multiple collections of NFT-like items which may be /// minted, burned and/or have attributes set on them. -pub trait Mutate: Inspect { +pub trait Mutate: Inspect { /// Mint some `item` of `collection` to be owned by `who`. /// /// By default, this is not a supported operation. @@ -167,7 +166,6 @@ pub trait Mutate: Inspect { _collection: &Self::CollectionId, _item: &Self::ItemId, _who: &AccountId, - _config: &ItemConfig, ) -> DispatchResult { Err(TokenError::Unsupported.into()) } diff --git a/frame/support/src/traits/tokens/nonfungibles_v2.rs b/frame/support/src/traits/tokens/nonfungibles_v2.rs new file mode 100644 index 0000000000000..d23e6d67573c7 --- /dev/null +++ b/frame/support/src/traits/tokens/nonfungibles_v2.rs @@ -0,0 +1,243 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits for dealing with multiple collections of non-fungible items. +//! +//! This assumes a dual-level namespace identified by `Inspect::ItemId`, and could +//! reasonably be implemented by pallets which want to expose multiple independent collections of +//! NFT-like objects. +//! +//! For an NFT API which has single-level namespacing, the traits in `nonfungible` are better to +//! use. +//! +//! Implementations of these traits may be converted to implementations of corresponding +//! `nonfungible` traits by using the `nonfungible::ItemOf` type adapter. + +use crate::dispatch::{DispatchError, DispatchResult}; +use codec::{Decode, Encode}; +use sp_runtime::TokenError; +use sp_std::prelude::*; + +/// Trait for providing an interface to many read-only NFT-like sets of items. +pub trait Inspect { + /// Type for identifying an item. + type ItemId; + + /// Type for identifying a collection (an identifier for an independent collection of + /// items). + type CollectionId; + + /// Returns the owner of `item` of `collection`, or `None` if the item doesn't exist + /// (or somehow has no owner). + fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option; + + /// Returns the owner of the `collection`, if there is one. For many NFTs this may not + /// make any sense, so users of this API should not be surprised to find a collection + /// results in `None` here. + fn collection_owner(_collection: &Self::CollectionId) -> Option { + None + } + + /// Returns the attribute value of `item` of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn attribute( + _collection: &Self::CollectionId, + _item: &Self::ItemId, + _key: &[u8], + ) -> Option> { + None + } + + /// Returns the strongly-typed attribute value of `item` of `collection` corresponding to + /// `key`. + /// + /// By default this just attempts to use `attribute`. + fn typed_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + key.using_encoded(|d| Self::attribute(collection, item, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns the attribute value of `collection` corresponding to `key`. + /// + /// By default this is `None`; no attributes are defined. + fn collection_attribute(_collection: &Self::CollectionId, _key: &[u8]) -> Option> { + None + } + + /// Returns the strongly-typed attribute value of `collection` corresponding to `key`. + /// + /// By default this just attempts to use `collection_attribute`. + fn typed_collection_attribute( + collection: &Self::CollectionId, + key: &K, + ) -> Option { + key.using_encoded(|d| Self::collection_attribute(collection, d)) + .and_then(|v| V::decode(&mut &v[..]).ok()) + } + + /// Returns `true` if the `item` of `collection` may be transferred. + /// + /// Default implementation is that all items are transferable. + fn can_transfer(_collection: &Self::CollectionId, _item: &Self::ItemId) -> bool { + true + } +} + +/// Interface for enumerating items in existence or owned by a given account over many collections +/// of NFTs. +pub trait InspectEnumerable: Inspect { + /// Returns an iterator of the collections in existence. + fn collections() -> Box>; + + /// Returns an iterator of the items of a `collection` in existence. + fn items(collection: &Self::CollectionId) -> Box>; + + /// Returns an iterator of the items of all collections owned by `who`. + fn owned(who: &AccountId) -> Box>; + + /// Returns an iterator of the items of `collection` owned by `who`. + fn owned_in_collection( + collection: &Self::CollectionId, + who: &AccountId, + ) -> Box>; +} + +/// Trait for providing the ability to create collections of nonfungible items. +pub trait Create: Inspect { + /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. + fn create_collection( + collection: &Self::CollectionId, + who: &AccountId, + admin: &AccountId, + config: &CollectionConfig, + ) -> DispatchResult; +} + +/// Trait for providing the ability to destroy collections of nonfungible items. +pub trait Destroy: Inspect { + /// The witness data needed to destroy an item. + type DestroyWitness; + + /// Provide the appropriate witness data needed to destroy an item. + fn get_destroy_witness(collection: &Self::CollectionId) -> Option; + + /// Destroy an existing fungible item. + /// * `collection`: The `CollectionId` to be destroyed. + /// * `witness`: Any witness data that needs to be provided to complete the operation + /// successfully. + /// * `maybe_check_owner`: An optional account id that can be used to authorize the destroy + /// command. If not provided, we will not do any authorization checks before destroying the + /// item. + /// + /// If successful, this function will return the actual witness data from the destroyed item. + /// This may be different than the witness data provided, and can be used to refund weight. + fn destroy( + collection: Self::CollectionId, + witness: Self::DestroyWitness, + maybe_check_owner: Option, + ) -> Result; +} + +/// Trait for providing an interface for multiple collections of NFT-like items which may be +/// minted, burned and/or have attributes set on them. +pub trait Mutate: Inspect { + /// Mint some `item` of `collection` to be owned by `who`. + /// + /// By default, this is not a supported operation. + fn mint_into( + _collection: &Self::CollectionId, + _item: &Self::ItemId, + _who: &AccountId, + _config: &ItemConfig, + ) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Burn some `item` of `collection`. + /// + /// By default, this is not a supported operation. + fn burn( + _collection: &Self::CollectionId, + _item: &Self::ItemId, + _maybe_check_owner: Option<&AccountId>, + ) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Set attribute `value` of `item` of `collection`'s `key`. + /// + /// By default, this is not a supported operation. + fn set_attribute( + _collection: &Self::CollectionId, + _item: &Self::ItemId, + _key: &[u8], + _value: &[u8], + ) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Attempt to set the strongly-typed attribute `value` of `item` of `collection`'s `key`. + /// + /// By default this just attempts to use `set_attribute`. + fn set_typed_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + value: &V, + ) -> DispatchResult { + key.using_encoded(|k| value.using_encoded(|v| Self::set_attribute(collection, item, k, v))) + } + + /// Set attribute `value` of `collection`'s `key`. + /// + /// By default, this is not a supported operation. + fn set_collection_attribute( + _collection: &Self::CollectionId, + _key: &[u8], + _value: &[u8], + ) -> DispatchResult { + Err(TokenError::Unsupported.into()) + } + + /// Attempt to set the strongly-typed attribute `value` of `collection`'s `key`. + /// + /// By default this just attempts to use `set_attribute`. + fn set_typed_collection_attribute( + collection: &Self::CollectionId, + key: &K, + value: &V, + ) -> DispatchResult { + key.using_encoded(|k| { + value.using_encoded(|v| Self::set_collection_attribute(collection, k, v)) + }) + } +} + +/// Trait for providing a non-fungible sets of items which can only be transferred. +pub trait Transfer: Inspect { + /// Transfer `item` of `collection` into `destination` account. + fn transfer( + collection: &Self::CollectionId, + item: &Self::ItemId, + destination: &AccountId, + ) -> DispatchResult; +} From 09aca134b746ef4aa5776fb470d4d5d0f6cbdf06 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:30:26 +0300 Subject: [PATCH 35/63] Fix traits --- frame/nfts/src/impl_nonfungibles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 7c7b6554ae731..45e544d460d2f 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -19,7 +19,7 @@ use super::*; use frame_support::{ - traits::{tokens::nonfungibles::*, Get}, + traits::{tokens::nonfungibles_v2::*, Get}, BoundedSlice, }; use sp_runtime::{DispatchError, DispatchResult}; From 114e51925cc586b36dbff56c51eede3263ed6040 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:30:50 +0300 Subject: [PATCH 36/63] Test SystemFeatures --- frame/nfts/src/mock.rs | 2 +- frame/nfts/src/tests.rs | 67 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index e4667a7265acb..ce5d24a13ad1a 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -86,7 +86,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); + pub storage FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); } impl Config for Test { diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 8f596cf78c9b8..52be09756dee2 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1327,3 +1327,70 @@ fn collection_locking_should_work() { assert_eq!(stored_config, full_lock_config); }); } + +#[test] +fn pallet_level_feature_flags_should_work() { + new_test_ext().execute_with(|| { + FeatureFlags::set(&SystemFeatures( + SystemFeature::NoTrading | SystemFeature::NoApprovals | SystemFeature::NoAttributes, + )); + + let user_id = 1; + let collection_id = 0; + let item_id = 1; + + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); + + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_id, + user_id, + default_item_config(), + )); + + // SystemFeature::NoTrading + assert_noop!( + Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_id, Some(1), None), + Error::::MethodDisabled + ); + assert_noop!( + Nfts::buy_item(RuntimeOrigin::signed(user_id), collection_id, item_id, 1), + Error::::MethodDisabled + ); + + // SystemFeature::NoApprovals + assert_noop!( + Nfts::approve_transfer(RuntimeOrigin::signed(user_id), collection_id, item_id, 2, None), + Error::::MethodDisabled + ); + assert_noop!( + Nfts::cancel_approval(RuntimeOrigin::signed(user_id), collection_id, item_id, 2), + Error::::MethodDisabled + ); + assert_noop!( + Nfts::clear_all_transfer_approvals( + RuntimeOrigin::signed(user_id), + collection_id, + item_id, + ), + Error::::MethodDisabled + ); + + // SystemFeature::NoAttributes + assert_noop!( + Nfts::set_attribute( + RuntimeOrigin::signed(user_id), + collection_id, + None, + bvec![0], + bvec![0] + ), + Error::::MethodDisabled + ); + assert_noop!( + Nfts::clear_attribute(RuntimeOrigin::signed(user_id), collection_id, None, bvec![0]), + Error::::MethodDisabled + ); + }) +} From d6536fc3041c01e4459fe0d1352406e678d8748b Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:31:05 +0300 Subject: [PATCH 37/63] Fix benchmarks --- frame/nfts/src/benchmarking.rs | 50 ++++++++++++++-------------------- frame/nfts/src/lib.rs | 28 +++++++++---------- 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index dbb257b74c5ad..21dcadf9b6a91 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -24,6 +24,7 @@ use frame_benchmarking::{ account, benchmarks_instance_pallet, whitelist_account, whitelisted_caller, }; use frame_support::{ + assert_ok, dispatch::UnfilteredDispatchable, traits::{EnsureOrigin, Get}, BoundedVec, @@ -42,8 +43,11 @@ fn create_collection, I: 'static>( let caller_lookup = T::Lookup::unlookup(caller.clone()); let collection = T::Helper::collection(0); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - assert!(Nfts::::force_create(SystemOrigin::Root.into(), caller_lookup.clone(), false,) - .is_ok()); + assert_ok!(Nfts::::force_create( + SystemOrigin::Root.into(), + caller_lookup.clone(), + CollectionConfig::empty() + )); (collection, caller, caller_lookup) } @@ -53,13 +57,11 @@ fn add_collection_metadata, I: 'static>() -> (T::AccountId, Account whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); - assert!(Nfts::::set_collection_metadata( + assert_ok!(Nfts::::set_collection_metadata( SystemOrigin::Signed(caller.clone()).into(), T::Helper::collection(0), vec![0; T::StringLimit::get() as usize].try_into().unwrap(), - false, - ) - .is_ok()); + )); (caller, caller_lookup) } @@ -72,13 +74,13 @@ fn mint_item, I: 'static>( } let caller_lookup = T::Lookup::unlookup(caller.clone()); let item = T::Helper::item(index); - assert!(Nfts::::mint( + assert_ok!(Nfts::::mint( SystemOrigin::Signed(caller.clone()).into(), T::Helper::collection(0), item, caller_lookup.clone(), - ) - .is_ok()); + ItemConfig::empty(), + )); (item, caller, caller_lookup) } @@ -90,14 +92,12 @@ fn add_item_metadata, I: 'static>( whitelist_account!(caller); } let caller_lookup = T::Lookup::unlookup(caller.clone()); - assert!(Nfts::::set_metadata( + assert_ok!(Nfts::::set_metadata( SystemOrigin::Signed(caller.clone()).into(), T::Helper::collection(0), item, vec![0; T::StringLimit::get() as usize].try_into().unwrap(), - false, - ) - .is_ok()); + )); (caller, caller_lookup) } @@ -110,14 +110,13 @@ fn add_item_attribute, I: 'static>( } let caller_lookup = T::Lookup::unlookup(caller.clone()); let key: BoundedVec<_, _> = vec![0; T::KeyLimit::get() as usize].try_into().unwrap(); - assert!(Nfts::::set_attribute( + assert_ok!(Nfts::::set_attribute( SystemOrigin::Signed(caller.clone()).into(), T::Helper::collection(0), Some(item), key.clone(), vec![0; T::ValueLimit::get() as usize].try_into().unwrap(), - ) - .is_ok()); + )); (key, caller, caller_lookup) } @@ -137,7 +136,7 @@ benchmarks_instance_pallet! { whitelist_account!(caller); let admin = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let call = Call::::create { admin }; + let call = Call::::create { admin, config: CollectionConfig::empty() }; }: { call.dispatch_bypass_filter(origin)? } verify { assert_last_event::(Event::Created { collection: T::Helper::collection(0), creator: caller.clone(), owner: caller }.into()); @@ -146,25 +145,19 @@ benchmarks_instance_pallet! { force_create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - }: _(SystemOrigin::Root, caller_lookup, true) + }: _(SystemOrigin::Root, caller_lookup, CollectionConfig::empty()) verify { assert_last_event::(Event::ForceCreated { collection: T::Helper::collection(0), owner: caller }.into()); } destroy { let n in 0 .. 1_000; - let m in 0 .. 1_000; - let a in 0 .. 1_000; let (collection, caller, caller_lookup) = create_collection::(); add_collection_metadata::(); for i in 0..n { mint_item::(i as u16); - } - for i in 0..m { add_item_metadata::(T::Helper::item(i as u16)); - } - for i in 0..a { add_item_attribute::(T::Helper::item(i as u16)); } let witness = Collection::::get(collection).unwrap().destroy_witness(); @@ -176,7 +169,7 @@ benchmarks_instance_pallet! { mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup) + }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, ItemConfig::empty()) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); } @@ -211,8 +204,7 @@ benchmarks_instance_pallet! { caller_lookup.clone(), caller_lookup.clone(), caller_lookup, - true, - CollectionConfig::empty(), + CollectionConfig(CollectionSetting::FreeHolding.into()), )?; }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone()) verify { @@ -288,11 +280,11 @@ benchmarks_instance_pallet! { issuer: caller_lookup.clone(), admin: caller_lookup.clone(), freezer: caller_lookup, - config: CollectionConfig(CollectionSetting::FreeHolding), + config: CollectionConfig(CollectionSetting::FreeHolding.into()), }; }: { call.dispatch_bypass_filter(origin)? } verify { - assert_last_event::(Event::ItemStatusChanged { collection }.into()); + assert_last_event::(Event::CollectionStatusChanged { collection }.into()); } set_attribute { diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index f39c53ad9a32d..bda4b0d541d6e 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1133,7 +1133,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::force_collection_status())] pub fn force_collection_status( origin: OriginFor, - collection_id: T::CollectionId, + collection: T::CollectionId, owner: AccountIdLookupOf, issuer: AccountIdLookupOf, admin: AccountIdLookupOf, @@ -1142,21 +1142,21 @@ pub mod pallet { ) -> DispatchResult { T::ForceOrigin::ensure_origin(origin)?; - Collection::::try_mutate(collection_id, |maybe_collection| { - let mut collection = + Collection::::try_mutate(collection, |maybe_collection| { + let mut collection_info = maybe_collection.take().ok_or(Error::::UnknownCollection)?; - let old_owner = collection.owner; + let old_owner = collection_info.owner; let new_owner = T::Lookup::lookup(owner)?; - collection.owner = new_owner.clone(); - collection.issuer = T::Lookup::lookup(issuer)?; - collection.admin = T::Lookup::lookup(admin)?; - collection.freezer = T::Lookup::lookup(freezer)?; - *maybe_collection = Some(collection); - CollectionAccount::::remove(&old_owner, &collection_id); - CollectionAccount::::insert(&new_owner, &collection_id, ()); - CollectionConfigOf::::insert(&collection_id, config); - - Self::deposit_event(Event::CollectionStatusChanged { collection: collection_id }); + collection_info.owner = new_owner.clone(); + collection_info.issuer = T::Lookup::lookup(issuer)?; + collection_info.admin = T::Lookup::lookup(admin)?; + collection_info.freezer = T::Lookup::lookup(freezer)?; + *maybe_collection = Some(collection_info); + CollectionAccount::::remove(&old_owner, &collection); + CollectionAccount::::insert(&new_owner, &collection, ()); + CollectionConfigOf::::insert(&collection, config); + + Self::deposit_event(Event::CollectionStatusChanged { collection }); Ok(()) }) } From 09a4ed6ec016da70ed33acd7b97a7cde880af058 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:39:28 +0300 Subject: [PATCH 38/63] Add missing benchmark --- frame/nfts/src/benchmarking.rs | 10 ++++++++++ frame/nfts/src/lib.rs | 2 +- frame/nfts/src/weights.rs | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 21dcadf9b6a91..9d9fd6f7c3958 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -287,6 +287,16 @@ benchmarks_instance_pallet! { assert_last_event::(Event::CollectionStatusChanged { collection }.into()); } + lock_item { + let (collection, caller, caller_lookup) = create_collection::(); + let (item, ..) = mint_item::(0); + let lock_metadata = true; + let lock_attributes = true; + }: _(SystemOrigin::Signed(caller), collection, item, lock_metadata, lock_attributes) + verify { + assert_last_event::(Event::ItemLocked { collection, item, lock_metadata, lock_attributes }.into()); + } + set_attribute { let key: BoundedVec<_, _> = vec![0u8; T::KeyLimit::get() as usize].try_into().unwrap(); let value: BoundedVec<_, _> = vec![0u8; T::ValueLimit::get() as usize].try_into().unwrap(); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index bda4b0d541d6e..b925b8f079bb6 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1174,7 +1174,7 @@ pub mod pallet { /// Emits `ItemLocked`. /// /// Weight: `O(1)` - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::lock_item())] pub fn lock_item( origin: OriginFor, collection: T::CollectionId, diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 27d770e7f9344..751dab1c0b718 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -59,6 +59,7 @@ pub trait WeightInfo { fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn force_collection_status() -> Weight; + fn lock_item() -> Weight; fn set_attribute() -> Weight; fn clear_attribute() -> Weight; fn set_metadata() -> Weight; @@ -199,6 +200,13 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ClassAccount (r:0 w:1) + fn lock_item() -> Weight { + Weight::from_ref_time(25_684_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: Nfts Class (r:1 w:1) // Storage: Nfts InstanceMetadataOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn set_attribute() -> Weight { @@ -423,6 +431,13 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ClassAccount (r:0 w:1) + fn lock_item() -> Weight { + Weight::from_ref_time(25_684_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: Nfts Class (r:1 w:1) // Storage: Nfts InstanceMetadataOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn set_attribute() -> Weight { From d408b32adf422913ec7e7a78087f3525accf3ad2 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 29 Sep 2022 14:53:45 +0300 Subject: [PATCH 39/63] Fix node/runtime/lib.rs --- bin/node/runtime/src/lib.rs | 4 ++-- frame/nfts/src/types.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0e52bd54dd912..289d0e34be999 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -53,7 +53,7 @@ use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; -use pallet_nfts::{SystemFeature, SystemFeatures}; +use pallet_nfts::SystemFeatures; use pallet_session::historical::{self as pallet_session_historical}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; @@ -1498,7 +1498,7 @@ impl pallet_uniques::Config for Runtime { } parameter_types! { - pub FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); + pub FeatureFlags: SystemFeatures = SystemFeatures::empty(); } impl pallet_nfts::Config for Runtime { diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 2774d2a6d1445..ee1541dc1c049 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -225,4 +225,10 @@ pub type SystemFeatureFlags = BitFlags; /// Wrapper type for `SystemFeatureFlags` that implements `Codec`. #[derive(Default, RuntimeDebug)] pub struct SystemFeatures(pub SystemFeatureFlags); + +impl SystemFeatures { + pub fn empty() -> Self { + Self(BitFlags::EMPTY) + } +} impl_codec_bitflags!(SystemFeatures, u64, SystemFeature); From fd86e419c1c393f289df783da42d210582619fab Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Mon, 3 Oct 2022 18:42:41 +0000 Subject: [PATCH 40/63] ".git/.scripts/bench-bot.sh" pallet dev pallet_nfts --- frame/nfts/src/weights.rs | 328 +++++++++++++++++++++----------------- 1 file changed, 182 insertions(+), 146 deletions(-) diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 751dab1c0b718..94da540014eab 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_nfts //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-24, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -48,7 +48,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(n: u32, m: u32, a: u32, ) -> Weight; + fn destroy(n: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -82,229 +82,247 @@ impl WeightInfo for SubstrateWeight { // Storage: Nfts NextCollectionId (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn create() -> Weight { - Weight::from_ref_time(37_627_000 as u64) + Weight::from_ref_time(39_252_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: Nfts NextCollectionId (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn force_create() -> Weight { - Weight::from_ref_time(25_748_000 as u64) + Weight::from_ref_time(27_479_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts Asset (r:1 w:0) // Storage: Nfts ClassAccount (r:0 w:1) - // Storage: Nfts Attribute (r:0 w:1000) // Storage: Nfts ClassMetadataOf (r:0 w:1) - // Storage: Nfts InstanceMetadataOf (r:0 w:1000) + // Storage: Nfts CollectionConfigOf (r:0 w:1) // Storage: Nfts CollectionMaxSupply (r:0 w:1) + // Storage: Nfts Attribute (r:0 w:20) + // Storage: Nfts InstanceMetadataOf (r:0 w:20) + // Storage: Nfts ItemConfigOf (r:0 w:20) // Storage: Nfts Account (r:0 w:20) /// The range of component `n` is `[0, 1000]`. - /// The range of component `m` is `[0, 1000]`. - /// The range of component `a` is `[0, 1000]`. - fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - Weight::from_ref_time(2_449_817_000 as u64) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(8_423_500 as u64).saturating_mul(n as u64)) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(315_839 as u64).saturating_mul(m as u64)) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(217_497 as u64).saturating_mul(a as u64)) + fn destroy(n: u32, ) -> Weight { + Weight::from_ref_time(55_419_000 as u64) + // Standard Error: 18_623 + .saturating_add(Weight::from_ref_time(12_843_237 as u64).saturating_mul(n as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(T::DbWeight::get().writes(2004 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(n as u64))) + .saturating_add(T::DbWeight::get().writes(5 as u64)) + .saturating_add(T::DbWeight::get().writes((5 as u64).saturating_mul(n as u64))) } // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts CollectionMaxSupply (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:0 w:1) // Storage: Nfts Account (r:0 w:1) fn mint() -> Weight { - Weight::from_ref_time(43_014_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + Weight::from_ref_time(47_947_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) + .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts Asset (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:0 w:1) // Storage: Nfts Account (r:0 w:1) // Storage: Nfts ItemPriceOf (r:0 w:1) fn burn() -> Weight { - Weight::from_ref_time(44_421_000 as u64) + Weight::from_ref_time(47_193_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(4 as u64)) + .saturating_add(T::DbWeight::get().writes(5 as u64)) } // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Account (r:0 w:2) // Storage: Nfts ItemPriceOf (r:0 w:1) fn transfer() -> Weight { - Weight::from_ref_time(34_315_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(42_305_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts Asset (r:102 w:102) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - Weight::from_ref_time(22_836_000 as u64) - // Standard Error: 9_131 - .saturating_add(Weight::from_ref_time(10_894_264 as u64).saturating_mul(i as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(26_327_000 as u64) + // Standard Error: 10_090 + .saturating_add(Weight::from_ref_time(10_876_864 as u64).saturating_mul(i as u64)) + .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(i as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(i as u64))) } - // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn freeze() -> Weight { - Weight::from_ref_time(27_329_000 as u64) + Weight::from_ref_time(28_194_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn thaw() -> Weight { - Weight::from_ref_time(27_842_000 as u64) + Weight::from_ref_time(28_821_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:1) fn lock_collection() -> Weight { - Weight::from_ref_time(23_129_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(25_896_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts OwnershipAcceptance (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:2) fn transfer_ownership() -> Weight { - Weight::from_ref_time(31_684_000 as u64) + Weight::from_ref_time(32_728_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) fn set_team() -> Weight { - Weight::from_ref_time(23_143_000 as u64) + Weight::from_ref_time(24_805_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn force_collection_status() -> Weight { - Weight::from_ref_time(25_684_000 as u64) + Weight::from_ref_time(28_468_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) } - // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn lock_item() -> Weight { - Weight::from_ref_time(25_684_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(27_377_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts InstanceMetadataOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn set_attribute() -> Weight { - Weight::from_ref_time(50_159_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) + Weight::from_ref_time(53_019_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts InstanceMetadataOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn clear_attribute() -> Weight { - Weight::from_ref_time(47_824_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) + Weight::from_ref_time(52_530_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts InstanceMetadataOf (r:1 w:1) fn set_metadata() -> Weight { - Weight::from_ref_time(39_968_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(48_054_000 as u64) + .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts InstanceMetadataOf (r:1 w:1) fn clear_metadata() -> Weight { - Weight::from_ref_time(42_182_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(46_590_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassMetadataOf (r:1 w:1) fn set_collection_metadata() -> Weight { - Weight::from_ref_time(39_330_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(44_281_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts ClassMetadataOf (r:1 w:1) fn clear_collection_metadata() -> Weight { - Weight::from_ref_time(38_351_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(42_355_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) + // Storage: Nfts CollectionConfigOf (r:1 w:0) fn approve_transfer() -> Weight { - Weight::from_ref_time(29_530_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(33_170_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) fn cancel_approval() -> Weight { - Weight::from_ref_time(29_417_000 as u64) + Weight::from_ref_time(31_121_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) fn clear_all_transfer_approvals() -> Weight { - Weight::from_ref_time(28_482_000 as u64) + Weight::from_ref_time(30_133_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts OwnershipAcceptance (r:1 w:1) fn set_accept_ownership() -> Weight { - Weight::from_ref_time(25_851_000 as u64) + Weight::from_ref_time(26_421_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts CollectionMaxSupply (r:1 w:1) // Storage: Nfts Class (r:1 w:0) fn set_collection_max_supply() -> Weight { - Weight::from_ref_time(24_836_000 as u64) + Weight::from_ref_time(26_358_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Asset (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts ItemPriceOf (r:0 w:1) fn set_price() -> Weight { - Weight::from_ref_time(25_665_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(33_607_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts ItemPriceOf (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Account (r:0 w:2) fn buy_item() -> Weight { - Weight::from_ref_time(47_502_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) + Weight::from_ref_time(54_511_000 as u64) + .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(4 as u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { - Weight::from_ref_time(5_417_000 as u64) - // Standard Error: 32_526 - .saturating_add(Weight::from_ref_time(4_304_363 as u64).saturating_mul(n as u64)) + Weight::from_ref_time(6_015_000 as u64) + // Standard Error: 34_307 + .saturating_add(Weight::from_ref_time(4_308_600 as u64).saturating_mul(n as u64)) } } @@ -313,228 +331,246 @@ impl WeightInfo for () { // Storage: Nfts NextCollectionId (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn create() -> Weight { - Weight::from_ref_time(37_627_000 as u64) + Weight::from_ref_time(39_252_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: Nfts NextCollectionId (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn force_create() -> Weight { - Weight::from_ref_time(25_748_000 as u64) + Weight::from_ref_time(27_479_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts Asset (r:1 w:0) // Storage: Nfts ClassAccount (r:0 w:1) - // Storage: Nfts Attribute (r:0 w:1000) // Storage: Nfts ClassMetadataOf (r:0 w:1) - // Storage: Nfts InstanceMetadataOf (r:0 w:1000) + // Storage: Nfts CollectionConfigOf (r:0 w:1) // Storage: Nfts CollectionMaxSupply (r:0 w:1) + // Storage: Nfts Attribute (r:0 w:20) + // Storage: Nfts InstanceMetadataOf (r:0 w:20) + // Storage: Nfts ItemConfigOf (r:0 w:20) // Storage: Nfts Account (r:0 w:20) /// The range of component `n` is `[0, 1000]`. - /// The range of component `m` is `[0, 1000]`. - /// The range of component `a` is `[0, 1000]`. - fn destroy(n: u32, m: u32, a: u32, ) -> Weight { - Weight::from_ref_time(2_449_817_000 as u64) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(8_423_500 as u64).saturating_mul(n as u64)) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(315_839 as u64).saturating_mul(m as u64)) - // Standard Error: 27_329 - .saturating_add(Weight::from_ref_time(217_497 as u64).saturating_mul(a as u64)) + fn destroy(n: u32, ) -> Weight { + Weight::from_ref_time(55_419_000 as u64) + // Standard Error: 18_623 + .saturating_add(Weight::from_ref_time(12_843_237 as u64).saturating_mul(n as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) - .saturating_add(RocksDbWeight::get().writes(2004 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(n as u64))) + .saturating_add(RocksDbWeight::get().writes(5 as u64)) + .saturating_add(RocksDbWeight::get().writes((5 as u64).saturating_mul(n as u64))) } // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts CollectionMaxSupply (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:0 w:1) // Storage: Nfts Account (r:0 w:1) fn mint() -> Weight { - Weight::from_ref_time(43_014_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + Weight::from_ref_time(47_947_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts Asset (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:0 w:1) // Storage: Nfts Account (r:0 w:1) // Storage: Nfts ItemPriceOf (r:0 w:1) fn burn() -> Weight { - Weight::from_ref_time(44_421_000 as u64) + Weight::from_ref_time(47_193_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(4 as u64)) + .saturating_add(RocksDbWeight::get().writes(5 as u64)) } // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Account (r:0 w:2) // Storage: Nfts ItemPriceOf (r:0 w:1) fn transfer() -> Weight { - Weight::from_ref_time(34_315_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(42_305_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts Asset (r:102 w:102) /// The range of component `i` is `[0, 5000]`. fn redeposit(i: u32, ) -> Weight { - Weight::from_ref_time(22_836_000 as u64) - // Standard Error: 9_131 - .saturating_add(Weight::from_ref_time(10_894_264 as u64).saturating_mul(i as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + Weight::from_ref_time(26_327_000 as u64) + // Standard Error: 10_090 + .saturating_add(Weight::from_ref_time(10_876_864 as u64).saturating_mul(i as u64)) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(i as u64))) .saturating_add(RocksDbWeight::get().writes(1 as u64)) .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(i as u64))) } - // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn freeze() -> Weight { - Weight::from_ref_time(27_329_000 as u64) + Weight::from_ref_time(28_194_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn thaw() -> Weight { - Weight::from_ref_time(27_842_000 as u64) + Weight::from_ref_time(28_821_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:1) fn lock_collection() -> Weight { - Weight::from_ref_time(23_129_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + Weight::from_ref_time(25_896_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts OwnershipAcceptance (r:1 w:1) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:2) fn transfer_ownership() -> Weight { - Weight::from_ref_time(31_684_000 as u64) + Weight::from_ref_time(32_728_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(4 as u64)) } // Storage: Nfts Class (r:1 w:1) fn set_team() -> Weight { - Weight::from_ref_time(23_143_000 as u64) + Weight::from_ref_time(24_805_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts CollectionConfigOf (r:0 w:1) fn force_collection_status() -> Weight { - Weight::from_ref_time(25_684_000 as u64) + Weight::from_ref_time(28_468_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) } - // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts ClassAccount (r:0 w:1) + // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:1) fn lock_item() -> Weight { - Weight::from_ref_time(25_684_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(27_377_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts InstanceMetadataOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn set_attribute() -> Weight { - Weight::from_ref_time(50_159_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) + Weight::from_ref_time(53_019_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) - // Storage: Nfts InstanceMetadataOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Attribute (r:1 w:1) fn clear_attribute() -> Weight { - Weight::from_ref_time(47_824_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) + Weight::from_ref_time(52_530_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts InstanceMetadataOf (r:1 w:1) fn set_metadata() -> Weight { - Weight::from_ref_time(39_968_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(48_054_000 as u64) + .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:1) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts InstanceMetadataOf (r:1 w:1) fn clear_metadata() -> Weight { - Weight::from_ref_time(42_182_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(46_590_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts Class (r:1 w:1) // Storage: Nfts ClassMetadataOf (r:1 w:1) fn set_collection_metadata() -> Weight { - Weight::from_ref_time(39_330_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(44_281_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) // Storage: Nfts ClassMetadataOf (r:1 w:1) fn clear_collection_metadata() -> Weight { - Weight::from_ref_time(38_351_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(42_355_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) + // Storage: Nfts CollectionConfigOf (r:1 w:0) fn approve_transfer() -> Weight { - Weight::from_ref_time(29_530_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(33_170_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) fn cancel_approval() -> Weight { - Weight::from_ref_time(29_417_000 as u64) + Weight::from_ref_time(31_121_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts Asset (r:1 w:1) fn clear_all_transfer_approvals() -> Weight { - Weight::from_ref_time(28_482_000 as u64) + Weight::from_ref_time(30_133_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts OwnershipAcceptance (r:1 w:1) fn set_accept_ownership() -> Weight { - Weight::from_ref_time(25_851_000 as u64) + Weight::from_ref_time(26_421_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts CollectionMaxSupply (r:1 w:1) // Storage: Nfts Class (r:1 w:0) fn set_collection_max_supply() -> Weight { - Weight::from_ref_time(24_836_000 as u64) + Weight::from_ref_time(26_358_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Asset (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts ItemPriceOf (r:0 w:1) fn set_price() -> Weight { - Weight::from_ref_time(25_665_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + Weight::from_ref_time(33_607_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Asset (r:1 w:1) // Storage: Nfts ItemPriceOf (r:1 w:1) // Storage: Nfts Class (r:1 w:0) + // Storage: Nfts CollectionConfigOf (r:1 w:0) + // Storage: Nfts ItemConfigOf (r:1 w:0) // Storage: Nfts Account (r:0 w:2) fn buy_item() -> Weight { - Weight::from_ref_time(47_502_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) + Weight::from_ref_time(54_511_000 as u64) + .saturating_add(RocksDbWeight::get().reads(5 as u64)) .saturating_add(RocksDbWeight::get().writes(4 as u64)) } /// The range of component `n` is `[0, 10]`. fn pay_tips(n: u32, ) -> Weight { - Weight::from_ref_time(5_417_000 as u64) - // Standard Error: 32_526 - .saturating_add(Weight::from_ref_time(4_304_363 as u64).saturating_mul(n as u64)) + Weight::from_ref_time(6_015_000 as u64) + // Standard Error: 34_307 + .saturating_add(Weight::from_ref_time(4_308_600 as u64).saturating_mul(n as u64)) } } From c002f8b58815128e13cd2f623d2eafe10c1f9e30 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 5 Oct 2022 14:21:02 +0300 Subject: [PATCH 41/63] Keep item's config on burn if it's not empty --- frame/nfts/src/functions.rs | 16 ++++++++++++++-- frame/nfts/src/lib.rs | 23 ++++++++++++++--------- frame/nfts/src/tests.rs | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index e952dc04cd68a..99e747662baef 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -192,7 +192,13 @@ impl, I: 'static> Pallet { let owner = owner.clone(); Account::::insert((&owner, &collection, &item), ()); - ItemConfigOf::::insert(&collection, &item, config); + + if let Ok(existing_config) = ItemConfigOf::::try_get(&collection, &item) { + ensure!(existing_config == config, Error::::InconsistentItemConfig); + } else { + ItemConfigOf::::insert(&collection, &item, config); + } + let details = ItemDetails { owner, approvals: ApprovalsOf::::default(), deposit }; Item::::insert(&collection, &item, details); @@ -229,7 +235,13 @@ impl, I: 'static> Pallet { Item::::remove(&collection, &item); Account::::remove((&owner, &collection, &item)); ItemPriceOf::::remove(&collection, &item); - ItemConfigOf::::remove(&collection, &item); + + // NOTE: if item's settings are not empty (e.g. item's metadata is locked) + // then we keep the record and don't remove it + let settings = Self::get_item_settings(&collection, &item)?; + if settings.is_empty() { + ItemConfigOf::::remove(&collection, &item); + } Self::deposit_event(Event::Burned { collection, item, owner }); Ok(()) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index b925b8f079bb6..a69f22ca63a46 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -483,6 +483,8 @@ pub mod pallet { ReachedApprovalLimit, /// The method is disabled by system settings. MethodDisabled, + /// Item's config already exists and should be equal to the provided one. + InconsistentItemConfig, } impl, I: 'static> Pallet { @@ -1301,11 +1303,12 @@ pub mod pallet { let collection_settings = Self::get_collection_settings(&collection)?; let maybe_is_frozen = match maybe_item { - None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), + None => collection_settings.contains(CollectionSetting::LockedAttributes), Some(item) => Self::get_item_settings(&collection, &item) - .map(|v| v.contains(ItemSetting::LockedAttributes)), - }?; - ensure!(!maybe_is_frozen, Error::::Frozen); + .map_or(false, |v| v.contains(ItemSetting::LockedAttributes)), + // NOTE: if the item was previously burned, the ItemSettings record might not exists + }; + ensure!(maybe_check_owner.is_none() || !maybe_is_frozen, Error::::Frozen); if let Some((_, deposit)) = Attribute::::take((collection, maybe_item, &key)) { collection_details.attributes.saturating_dec(); @@ -1389,7 +1392,7 @@ pub mod pallet { /// Clear the metadata for an item. /// /// Origin must be either `ForceOrigin` or Signed and the sender should be the Owner of the - /// `item`. + /// `collection`. /// /// Any deposit is freed for the collection's owner. /// @@ -1415,15 +1418,17 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let (action_allowed, _) = - Self::is_item_setting_disabled(&collection, &item, ItemSetting::LockedMetadata)?; - ensure!(maybe_check_owner.is_none() || action_allowed, Error::::Frozen); + // NOTE: if the item was previously burned, the ItemSettings record might not exists + let is_frozen = Self::get_item_settings(&collection, &item) + .map_or(false, |v| v.contains(ItemSetting::LockedMetadata)); + + ensure!(maybe_check_owner.is_none() || !is_frozen, Error::::Frozen); ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { if metadata.is_some() { collection_details.item_metadatas.saturating_dec(); } - let deposit = metadata.take().ok_or(Error::::UnknownCollection)?.deposit; + let deposit = metadata.take().ok_or(Error::::UnknownItem)?.deposit; T::Currency::unreserve(&collection_details.owner, deposit); collection_details.total_deposit.saturating_reduce(deposit); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 52be09756dee2..49b712bf963b2 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -563,6 +563,40 @@ fn set_attribute_should_respect_freeze() { }); } +#[test] +fn preserve_config_for_frozen_items() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&1, 100); + + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, default_item_config())); + + // if the item is not locked/frozen then the config gets deleted on item burn + assert_ok!(Nfts::burn(RuntimeOrigin::signed(1), 0, 1, Some(1))); + assert!(!ItemConfigOf::::contains_key(0, 1)); + + // lock the item and ensure the config stays unchanged + assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 0, true, true)); + + let expect_config = ItemConfig(ItemSetting::LockedAttributes | ItemSetting::LockedMetadata); + let config = ItemConfigOf::::get(0, 0).unwrap(); + assert_eq!(config, expect_config); + + assert_ok!(Nfts::burn(RuntimeOrigin::signed(1), 0, 0, Some(1))); + let config = ItemConfigOf::::get(0, 0).unwrap(); + assert_eq!(config, expect_config); + + // can't mint with the different config + assert_noop!( + Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config()), + Error::::InconsistentItemConfig + ); + + assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, expect_config)); + }); +} + #[test] fn force_collection_status_should_work() { new_test_ext().execute_with(|| { From d0ee824bdb8d0cc3133197e5d339aa6612eb3a58 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 5 Oct 2022 14:42:23 +0300 Subject: [PATCH 42/63] Fix the merge artifacts --- frame/nfts/src/tests.rs | 30 +++++++++++++++++++++--------- frame/nfts/src/weights.rs | 18 +++++++++++++++--- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index e4bad2eafc1c0..dfc01bba84449 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1315,10 +1315,22 @@ fn create_cancel_swap_should_work() { let duration = 2; let expect_deadline = 3; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_1, user_id)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_id), collection_id, item_2, user_id)); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_1, + user_id, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_id), + collection_id, + item_2, + user_id, + default_item_config(), + )); // validate desired item and the collection exists assert_noop!( @@ -1453,13 +1465,13 @@ fn claim_swap_should_work() { 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, true)); + assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_2)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_3, user_2)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_4, user_1)); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_5, user_2)); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_2, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_3, user_2, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_4, user_1, default_item_config())); + assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_5, user_2, default_item_config())); assert_ok!(Nfts::create_swap( RuntimeOrigin::signed(user_1), diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index d00cc1957cdfa..8d74683834571 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -48,7 +48,7 @@ use sp_std::marker::PhantomData; pub trait WeightInfo { fn create() -> Weight; fn force_create() -> Weight; - fn destroy(n: u32, ) -> Weight; + fn destroy(n: u32, m: u32, a: u32, ) -> Weight; fn mint() -> Weight; fn burn() -> Weight; fn transfer() -> Weight; @@ -111,10 +111,16 @@ impl WeightInfo for SubstrateWeight { // Storage: Nfts ItemConfigOf (r:0 w:20) // Storage: Nfts Account (r:0 w:20) /// The range of component `n` is `[0, 1000]`. - fn destroy(n: u32, ) -> Weight { + /// The range of component `m` is `[0, 1000]`. + /// The range of component `a` is `[0, 1000]`. + fn destroy(n: u32, m: u32, a: u32, ) -> Weight { Weight::from_ref_time(55_419_000 as u64) // Standard Error: 18_623 .saturating_add(Weight::from_ref_time(12_843_237 as u64).saturating_mul(n as u64)) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(315_839 as u64).saturating_mul(m as u64)) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(217_497 as u64).saturating_mul(a as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(n as u64))) .saturating_add(T::DbWeight::get().writes(5 as u64)) @@ -387,10 +393,16 @@ impl WeightInfo for () { // Storage: Nfts ItemConfigOf (r:0 w:20) // Storage: Nfts Account (r:0 w:20) /// The range of component `n` is `[0, 1000]`. - fn destroy(n: u32, ) -> Weight { + /// The range of component `m` is `[0, 1000]`. + /// The range of component `a` is `[0, 1000]`. + fn destroy(n: u32, m: u32, a: u32, ) -> Weight { Weight::from_ref_time(55_419_000 as u64) // Standard Error: 18_623 .saturating_add(Weight::from_ref_time(12_843_237 as u64).saturating_mul(n as u64)) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(315_839 as u64).saturating_mul(m as u64)) + // Standard Error: 27_329 + .saturating_add(Weight::from_ref_time(217_497 as u64).saturating_mul(a as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(n as u64))) .saturating_add(RocksDbWeight::get().writes(5 as u64)) From 0eb568ed06508cc7921a19cba7e7e65ece11b828 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Wed, 5 Oct 2022 14:44:38 +0300 Subject: [PATCH 43/63] Fmt --- frame/nfts/src/tests.rs | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index dfc01bba84449..0db08262835a1 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1467,11 +1467,41 @@ fn claim_swap_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_1, default_collection_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_1, user_1, default_item_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_2, user_2, default_item_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_3, user_2, default_item_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_4, user_1, default_item_config())); - assert_ok!(Nfts::mint(RuntimeOrigin::signed(user_1), collection_id, item_5, user_2, default_item_config())); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_1, + user_1, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_2, + user_2, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_3, + user_2, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_4, + user_1, + default_item_config(), + )); + assert_ok!(Nfts::mint( + RuntimeOrigin::signed(user_1), + collection_id, + item_5, + user_2, + default_item_config(), + )); assert_ok!(Nfts::create_swap( RuntimeOrigin::signed(user_1), From 998691ea358b1b1091b04993d02b435da285628b Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Thu, 6 Oct 2022 10:39:47 +0300 Subject: [PATCH 44/63] Add SystemFeature::NoSwaps check --- frame/nfts/src/features/atomic_swap.rs | 3 +++ frame/nfts/src/lib.rs | 12 ------------ frame/nfts/src/tests.rs | 16 ---------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/frame/nfts/src/features/atomic_swap.rs b/frame/nfts/src/features/atomic_swap.rs index 116da57477f4e..79b9fa9bee1e8 100644 --- a/frame/nfts/src/features/atomic_swap.rs +++ b/frame/nfts/src/features/atomic_swap.rs @@ -31,6 +31,7 @@ impl, I: 'static> Pallet { maybe_price: Option>>, duration: ::BlockNumber, ) -> DispatchResult { + ensure!(!Self::is_feature_flag_set(SystemFeature::NoSwaps), Error::::MethodDisabled); ensure!(duration <= T::MaxDeadlineDuration::get(), Error::::WrongDuration); let item = Item::::get(&offered_collection_id, &offered_item_id) @@ -111,6 +112,8 @@ impl, I: 'static> Pallet { receive_item_id: T::ItemId, witness_price: Option>>, ) -> DispatchResult { + ensure!(!Self::is_feature_flag_set(SystemFeature::NoSwaps), Error::::MethodDisabled); + let send_item = Item::::get(&send_collection_id, &send_item_id) .ok_or(Error::::UnknownItem)?; let receive_item = Item::::get(&receive_collection_id, &receive_item_id) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index bc5eea62f78ac..f7a655805e993 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1082,10 +1082,6 @@ pub mod pallet { item: T::ItemId, delegate: AccountIdLookupOf, ) -> DispatchResult { - ensure!( - !Self::is_feature_flag_set(SystemFeature::NoApprovals), - Error::::MethodDisabled - ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; @@ -1146,10 +1142,6 @@ pub mod pallet { collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { - ensure!( - !Self::is_feature_flag_set(SystemFeature::NoApprovals), - Error::::MethodDisabled - ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some).map_err(DispatchError::from))?; @@ -1343,10 +1335,6 @@ pub mod pallet { maybe_item: Option, key: BoundedVec, ) -> DispatchResult { - ensure!( - !Self::is_feature_flag_set(SystemFeature::NoAttributes), - Error::::MethodDisabled - ); let maybe_check_owner = T::ForceOrigin::try_origin(origin) .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 0db08262835a1..7a9c2cf34ea71 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1753,18 +1753,6 @@ fn pallet_level_feature_flags_should_work() { Nfts::approve_transfer(RuntimeOrigin::signed(user_id), collection_id, item_id, 2, None), Error::::MethodDisabled ); - assert_noop!( - Nfts::cancel_approval(RuntimeOrigin::signed(user_id), collection_id, item_id, 2), - Error::::MethodDisabled - ); - assert_noop!( - Nfts::clear_all_transfer_approvals( - RuntimeOrigin::signed(user_id), - collection_id, - item_id, - ), - Error::::MethodDisabled - ); // SystemFeature::NoAttributes assert_noop!( @@ -1777,9 +1765,5 @@ fn pallet_level_feature_flags_should_work() { ), Error::::MethodDisabled ); - assert_noop!( - Nfts::clear_attribute(RuntimeOrigin::signed(user_id), collection_id, None, bvec![0]), - Error::::MethodDisabled - ); }) } From b8c6f5ea922262b9f6cee87abbd0a1d6effe1f87 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 7 Oct 2022 10:41:20 +0300 Subject: [PATCH 45/63] Rename SystemFeatures to PalletFeatures --- bin/node/runtime/src/lib.rs | 6 +++--- frame/nfts/src/features/atomic_swap.rs | 10 ++++++++-- frame/nfts/src/features/settings.rs | 6 +++--- frame/nfts/src/functions.rs | 4 ++-- frame/nfts/src/lib.rs | 6 +++--- frame/nfts/src/mock.rs | 5 ++--- frame/nfts/src/tests.rs | 10 +++++----- frame/nfts/src/types.rs | 11 +++++------ 8 files changed, 31 insertions(+), 27 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 84efdad01e13d..f9c0686504507 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -53,7 +53,7 @@ use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; -use pallet_nfts::SystemFeatures; +use pallet_nfts::PalletFeatures; use pallet_session::historical::{self as pallet_session_historical}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; @@ -1499,7 +1499,7 @@ impl pallet_uniques::Config for Runtime { } parameter_types! { - pub FeatureFlags: SystemFeatures = SystemFeatures::empty(); + pub Features: PalletFeatures = PalletFeatures::empty(); } impl pallet_nfts::Config for Runtime { @@ -1519,7 +1519,7 @@ impl pallet_nfts::Config for Runtime { type ApprovalsLimit = ApprovalsLimit; type MaxTips = MaxTips; type MaxDeadlineDuration = MaxDeadlineDuration; - type FeatureFlags = FeatureFlags; + type Features = Features; type WeightInfo = pallet_nfts::weights::SubstrateWeight; #[cfg(feature = "runtime-benchmarks")] type Helper = (); diff --git a/frame/nfts/src/features/atomic_swap.rs b/frame/nfts/src/features/atomic_swap.rs index 79b9fa9bee1e8..fc8d321d21094 100644 --- a/frame/nfts/src/features/atomic_swap.rs +++ b/frame/nfts/src/features/atomic_swap.rs @@ -31,7 +31,10 @@ impl, I: 'static> Pallet { maybe_price: Option>>, duration: ::BlockNumber, ) -> DispatchResult { - ensure!(!Self::is_feature_flag_set(SystemFeature::NoSwaps), Error::::MethodDisabled); + ensure!( + Self::is_pallet_feature_disabled(PalletFeature::NoSwaps), + Error::::MethodDisabled + ); ensure!(duration <= T::MaxDeadlineDuration::get(), Error::::WrongDuration); let item = Item::::get(&offered_collection_id, &offered_item_id) @@ -112,7 +115,10 @@ impl, I: 'static> Pallet { receive_item_id: T::ItemId, witness_price: Option>>, ) -> DispatchResult { - ensure!(!Self::is_feature_flag_set(SystemFeature::NoSwaps), Error::::MethodDisabled); + ensure!( + Self::is_pallet_feature_disabled(PalletFeature::NoSwaps), + Error::::MethodDisabled + ); let send_item = Item::::get(&send_collection_id, &send_item_id) .ok_or(Error::::UnknownItem)?; diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index b4f7bff165597..5b3326265c202 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -53,8 +53,8 @@ impl, I: 'static> Pallet { Ok((!settings.contains(setting), settings)) } - pub fn is_feature_flag_set(feature: SystemFeature) -> bool { - let features = T::FeatureFlags::get(); - return features.0.contains(feature) + pub fn is_pallet_feature_disabled(feature: PalletFeature) -> bool { + let features = T::Features::get(); + return !features.0.contains(feature) } } diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 1c0371617dd13..44e7e5df98d61 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -259,7 +259,7 @@ impl, I: 'static> Pallet { whitelisted_buyer: Option, ) -> DispatchResult { ensure!( - !Self::is_feature_flag_set(SystemFeature::NoTrading), + Self::is_pallet_feature_disabled(PalletFeature::NoTrading), Error::::MethodDisabled ); @@ -299,7 +299,7 @@ impl, I: 'static> Pallet { bid_price: ItemPrice, ) -> DispatchResult { ensure!( - !Self::is_feature_flag_set(SystemFeature::NoTrading), + Self::is_pallet_feature_disabled(PalletFeature::NoTrading), Error::::MethodDisabled ); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index f7a655805e993..badaf33c9bba7 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -165,7 +165,7 @@ pub mod pallet { /// Disables some of pallet's features. #[pallet::constant] - type FeatureFlags: Get; + type Features: Get; #[cfg(feature = "runtime-benchmarks")] /// A set of helper functions for benchmarking. @@ -1015,7 +1015,7 @@ pub mod pallet { maybe_deadline: Option<::BlockNumber>, ) -> DispatchResult { ensure!( - !Self::is_feature_flag_set(SystemFeature::NoApprovals), + Self::is_pallet_feature_disabled(PalletFeature::NoApprovals), Error::::MethodDisabled ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) @@ -1265,7 +1265,7 @@ pub mod pallet { value: BoundedVec, ) -> DispatchResult { ensure!( - !Self::is_feature_flag_set(SystemFeature::NoAttributes), + Self::is_pallet_feature_disabled(PalletFeature::NoAttributes), Error::::MethodDisabled ); let maybe_check_owner = T::ForceOrigin::try_origin(origin) diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index b7e5d4be4c336..431d05a762be9 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -20,7 +20,6 @@ use super::*; use crate as pallet_nfts; -use enumflags2::BitFlag; use frame_support::{ construct_runtime, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64}, @@ -86,7 +85,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub storage FeatureFlags: SystemFeatures = SystemFeatures(SystemFeature::empty()); + pub storage Features: PalletFeatures = PalletFeatures::empty(); } impl Config for Test { @@ -108,7 +107,7 @@ impl Config for Test { type ApprovalsLimit = ConstU32<10>; type MaxTips = ConstU32<10>; type MaxDeadlineDuration = ConstU64<10000>; - type FeatureFlags = FeatureFlags; + type Features = Features; type WeightInfo = (); #[cfg(feature = "runtime-benchmarks")] type Helper = (); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 7a9c2cf34ea71..fc8c990848cf9 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1720,8 +1720,8 @@ fn collection_locking_should_work() { #[test] fn pallet_level_feature_flags_should_work() { new_test_ext().execute_with(|| { - FeatureFlags::set(&SystemFeatures( - SystemFeature::NoTrading | SystemFeature::NoApprovals | SystemFeature::NoAttributes, + Features::set(&PalletFeatures( + PalletFeature::NoTrading | PalletFeature::NoApprovals | PalletFeature::NoAttributes, )); let user_id = 1; @@ -1738,7 +1738,7 @@ fn pallet_level_feature_flags_should_work() { default_item_config(), )); - // SystemFeature::NoTrading + // PalletFeature::NoTrading assert_noop!( Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_id, Some(1), None), Error::::MethodDisabled @@ -1748,13 +1748,13 @@ fn pallet_level_feature_flags_should_work() { Error::::MethodDisabled ); - // SystemFeature::NoApprovals + // PalletFeature::NoApprovals assert_noop!( Nfts::approve_transfer(RuntimeOrigin::signed(user_id), collection_id, item_id, 2, None), Error::::MethodDisabled ); - // SystemFeature::NoAttributes + // PalletFeature::NoAttributes assert_noop!( Nfts::set_attribute( RuntimeOrigin::signed(user_id), diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 7c3e3c9d17060..dc4d76ff0ad46 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -234,7 +234,7 @@ impl_codec_bitflags!(ItemConfig, u64, ItemSetting); #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] -pub enum SystemFeature { +pub enum PalletFeature { /// Disallow trading operations. NoTrading, /// Disallow setting attributes. @@ -246,15 +246,14 @@ pub enum SystemFeature { /// Disallow public mints. NoPublicMints, } -pub type SystemFeatureFlags = BitFlags; -/// Wrapper type for `SystemFeatureFlags` that implements `Codec`. +/// Wrapper type for `BitFlags` that implements `Codec`. #[derive(Default, RuntimeDebug)] -pub struct SystemFeatures(pub SystemFeatureFlags); +pub struct PalletFeatures(pub BitFlags); -impl SystemFeatures { +impl PalletFeatures { pub fn empty() -> Self { Self(BitFlags::EMPTY) } } -impl_codec_bitflags!(SystemFeatures, u64, SystemFeature); +impl_codec_bitflags!(PalletFeatures, u64, PalletFeature); From 8bef776916c4ecab537133302cca23d253ab11f0 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 7 Oct 2022 11:17:19 +0300 Subject: [PATCH 46/63] Rename errors --- frame/nfts/src/functions.rs | 6 ++-- frame/nfts/src/lib.rs | 70 +++++++++++++++++++++++++------------ frame/nfts/src/tests.rs | 27 +++++++------- 3 files changed, 66 insertions(+), 37 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 44e7e5df98d61..ed9f351bceade 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -36,7 +36,7 @@ impl, I: 'static> Pallet { ) -> DispatchResult { let collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(!T::Locker::is_locked(collection, item), Error::::Locked); + ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); let (action_allowed, _) = Self::is_collection_setting_disabled( &collection, @@ -46,7 +46,7 @@ impl, I: 'static> Pallet { let (action_allowed, _) = Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; - ensure!(action_allowed, Error::::Locked); + ensure!(action_allowed, Error::::ItemLocked); let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; @@ -274,7 +274,7 @@ impl, I: 'static> Pallet { let (action_allowed, _) = Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; - ensure!(action_allowed, Error::::Locked); + ensure!(action_allowed, Error::::ItemLocked); if let Some(ref price) = price { ItemPriceOf::::insert(&collection, &item, (price, whitelisted_buyer.clone())); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index badaf33c9bba7..f142b13a5bbe8 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -514,9 +514,15 @@ pub mod pallet { /// The named owner has not signed ownership of the collection is acceptable. Unaccepted, /// The item is locked. - Locked, - /// The collection's metadata is locked. - CollectionMetadataIsLocked, + ItemLocked, + /// Item's attributes are locked. + LockedItemAttributes, + /// Collection's attributes are locked. + LockedCollectionAttributes, + /// Item's metadata is locked. + LockedItemMetadata, + /// Collection's metadata is locked. + LockedCollectionMetadata, /// All items have been minted. MaxSupplyReached, /// The max supply has already been set. @@ -1280,12 +1286,19 @@ pub mod pallet { } let collection_settings = Self::get_collection_settings(&collection)?; - let maybe_is_frozen = match maybe_item { - None => Ok(collection_settings.contains(CollectionSetting::LockedAttributes)), - Some(item) => Self::get_item_settings(&collection, &item) - .map(|v| v.contains(ItemSetting::LockedAttributes)), - }?; - ensure!(!maybe_is_frozen, Error::::Frozen); + match maybe_item { + None => { + ensure!( + !collection_settings.contains(CollectionSetting::LockedAttributes), + Error::::LockedCollectionAttributes + ) + }, + Some(item) => { + let maybe_is_locked = Self::get_item_settings(&collection, &item) + .map(|v| v.contains(ItemSetting::LockedAttributes))?; + ensure!(!maybe_is_locked, Error::::LockedItemAttributes); + }, + }; let attribute = Attribute::::get((collection, maybe_item, &key)); if attribute.is_none() { @@ -1345,14 +1358,24 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let collection_settings = Self::get_collection_settings(&collection)?; - let maybe_is_frozen = match maybe_item { - None => collection_settings.contains(CollectionSetting::LockedAttributes), - Some(item) => Self::get_item_settings(&collection, &item) - .map_or(false, |v| v.contains(ItemSetting::LockedAttributes)), - // NOTE: if the item was previously burned, the ItemSettings record might not exists - }; - ensure!(maybe_check_owner.is_none() || !maybe_is_frozen, Error::::Frozen); + if maybe_check_owner.is_some() { + match maybe_item { + None => { + let collection_settings = Self::get_collection_settings(&collection)?; + ensure!( + !collection_settings.contains(CollectionSetting::LockedAttributes), + Error::::LockedCollectionAttributes + ) + }, + Some(item) => { + // NOTE: if the item was previously burned, the ItemSettings record might + // not exists. In that case, we allow to clear the attribute. + let maybe_is_locked = Self::get_item_settings(&collection, &item) + .map_or(false, |v| v.contains(ItemSetting::LockedAttributes)); + ensure!(!maybe_is_locked, Error::::LockedItemAttributes); + }, + }; + } if let Some((_, deposit)) = Attribute::::take((collection, maybe_item, &key)) { collection_details.attributes.saturating_dec(); @@ -1396,7 +1419,10 @@ pub mod pallet { let (action_allowed, _) = Self::is_item_setting_disabled(&collection, &item, ItemSetting::LockedMetadata)?; - ensure!(maybe_check_owner.is_none() || action_allowed, Error::::Frozen); + ensure!( + maybe_check_owner.is_none() || action_allowed, + Error::::LockedItemMetadata + ); let collection_settings = Self::get_collection_settings(&collection)?; @@ -1463,10 +1489,10 @@ pub mod pallet { } // NOTE: if the item was previously burned, the ItemSettings record might not exists - let is_frozen = Self::get_item_settings(&collection, &item) + let is_locked = Self::get_item_settings(&collection, &item) .map_or(false, |v| v.contains(ItemSetting::LockedMetadata)); - ensure!(maybe_check_owner.is_none() || !is_frozen, Error::::Frozen); + ensure!(maybe_check_owner.is_none() || !is_locked, Error::::LockedItemMetadata); ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { if metadata.is_some() { @@ -1513,7 +1539,7 @@ pub mod pallet { )?; ensure!( maybe_check_owner.is_none() || action_allowed, - Error::::CollectionMetadataIsLocked + Error::::LockedCollectionMetadata ); let mut details = @@ -1581,7 +1607,7 @@ pub mod pallet { )?; ensure!( maybe_check_owner.is_none() || action_allowed, - Error::::CollectionMetadataIsLocked + Error::::LockedCollectionMetadata ); CollectionMetadataOf::::try_mutate_exists(collection, |metadata| { diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index fc8c990848cf9..3ed75af423102 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -230,7 +230,7 @@ fn freezing_should_work() { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_ok!(Nfts::freeze(RuntimeOrigin::signed(1), 0, 42)); - assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::Locked); + assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::ItemLocked); assert_ok!(Nfts::thaw(RuntimeOrigin::signed(1), 0, 42)); assert_ok!(Nfts::lock_collection( @@ -394,11 +394,11 @@ fn set_collection_metadata_should_work() { )); assert_noop!( Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15]), - Error::::CollectionMetadataIsLocked, + Error::::LockedCollectionMetadata, ); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), - Error::::CollectionMetadataIsLocked + Error::::LockedCollectionMetadata ); // Clear Metadata @@ -413,7 +413,7 @@ fn set_collection_metadata_should_work() { ); assert_noop!( Nfts::clear_collection_metadata(RuntimeOrigin::signed(1), 0), - Error::::CollectionMetadataIsLocked + Error::::LockedCollectionMetadata ); assert_ok!(Nfts::clear_collection_metadata(RuntimeOrigin::root(), 0)); assert!(!CollectionMetadataOf::::contains_key(0)); @@ -459,19 +459,22 @@ fn set_item_metadata_should_work() { assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 42, true, false)); assert_noop!( Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15]), - Error::::Frozen, + Error::::LockedItemMetadata, + ); + assert_noop!( + Nfts::clear_metadata(RuntimeOrigin::signed(1), 0, 42), + Error::::LockedItemMetadata, ); - assert_noop!(Nfts::clear_metadata(RuntimeOrigin::signed(1), 0, 42), Error::::Frozen); // Clear Metadata assert_ok!(Nfts::set_metadata(RuntimeOrigin::root(), 0, 42, bvec![0u8; 15])); assert_noop!( Nfts::clear_metadata(RuntimeOrigin::signed(2), 0, 42), - Error::::NoPermission + Error::::NoPermission, ); assert_noop!( Nfts::clear_metadata(RuntimeOrigin::signed(1), 1, 42), - Error::::UnknownCollection + Error::::UnknownCollection, ); assert_ok!(Nfts::clear_metadata(RuntimeOrigin::root(), 0, 42)); assert!(!ItemMetadataOf::::contains_key(0, 42)); @@ -525,7 +528,7 @@ fn set_attribute_should_work() { } #[test] -fn set_attribute_should_respect_freeze() { +fn set_attribute_should_respect_lock() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); @@ -553,12 +556,12 @@ fn set_attribute_should_respect_freeze() { CollectionConfig(CollectionSetting::LockedAttributes.into()) )); - let e = Error::::Frozen; + let e = Error::::LockedCollectionAttributes; assert_noop!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0]), e); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1])); assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 0, false, true)); - let e = Error::::Frozen; + let e = Error::::LockedItemAttributes; assert_noop!( Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1]), e @@ -1250,7 +1253,7 @@ fn buy_item_should_work() { }); assert_noop!( buy_item_call.dispatch(RuntimeOrigin::signed(user_2)), - Error::::Locked + Error::::ItemLocked ); } }); From ddaa884f99df2b4a3bfdd4117fcd6c16cf365c54 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 7 Oct 2022 11:32:49 +0300 Subject: [PATCH 47/63] Add docs --- frame/nfts/src/features/freeze.rs | 3 +++ frame/nfts/src/features/lock.rs | 3 +++ frame/nfts/src/features/settings.rs | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs index 6884911321ab5..264fdf77e6a73 100644 --- a/frame/nfts/src/features/freeze.rs +++ b/frame/nfts/src/features/freeze.rs @@ -18,6 +18,9 @@ use crate::*; use frame_support::pallet_prelude::*; +/// Freeze functions allow to make particular items non-transferable. +/// It's also possible to revert the setting back. +/// An origin must have a `Freezer` role in order to call those methods. impl, I: 'static> Pallet { pub fn do_freeze_item( origin: T::AccountId, diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index 2721d64ef11c3..f9c24711fe9f0 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -18,6 +18,9 @@ use crate::*; use frame_support::pallet_prelude::*; +/// Lock functions allow to lock collection/items metadata and attributes. +/// Additionally, it's possible to make all collection items non-transferable. +/// Those settings are irreversible. impl, I: 'static> Pallet { pub fn do_lock_collection( origin: T::AccountId, diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 5b3326265c202..c5e906ce1b29e 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -18,6 +18,10 @@ use crate::*; use frame_support::pallet_prelude::*; +/// The helper methods bellow allow to read and validate different +/// collection/item/pallet settings. +/// For example, those settings allow to disable NFTs trading on a pallet level, or for a particular +/// collection, or for a specific item. impl, I: 'static> Pallet { pub fn get_collection_settings( collection_id: &T::CollectionId, From 4c01fc51aeb1f6ce739d2fbd218bd67a49764882 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 7 Oct 2022 15:37:48 +0300 Subject: [PATCH 48/63] Change error message --- frame/nfts/src/features/lock.rs | 2 +- frame/nfts/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index f9c24711fe9f0..9e5cb727cc081 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -32,7 +32,7 @@ impl, I: 'static> Pallet { ensure!(origin == details.freezer, Error::::NoPermission); CollectionConfigOf::::try_mutate(collection, |maybe_config| { - let config = maybe_config.as_mut().ok_or(Error::::UnknownCollection)?; + let config = maybe_config.as_mut().ok_or(Error::::NoConfig)?; let mut settings = config.values(); let lock_settings = lock_config.values(); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index f142b13a5bbe8..76a5664889ec6 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -547,6 +547,8 @@ pub mod pallet { MethodDisabled, /// Item's config already exists and should be equal to the provided one. InconsistentItemConfig, + /// Config for a collection or an item can't be found. + NoConfig, } impl, I: 'static> Pallet { From fa9d471f4f7941b3e9102f680372ff761db8f823 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 13:39:13 +0300 Subject: [PATCH 49/63] Rework pallet features --- bin/node/runtime/src/lib.rs | 2 +- frame/nfts/src/features/atomic_swap.rs | 4 ++-- frame/nfts/src/features/settings.rs | 2 +- frame/nfts/src/functions.rs | 4 ++-- frame/nfts/src/lib.rs | 4 ++-- frame/nfts/src/mock.rs | 2 +- frame/nfts/src/tests.rs | 10 +++++----- frame/nfts/src/types.rs | 25 ++++++++++++++----------- 8 files changed, 28 insertions(+), 25 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f9c0686504507..0a7aca0f3bd7b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1499,7 +1499,7 @@ impl pallet_uniques::Config for Runtime { } parameter_types! { - pub Features: PalletFeatures = PalletFeatures::empty(); + pub Features: PalletFeatures = PalletFeatures::all_enabled(); } impl pallet_nfts::Config for Runtime { diff --git a/frame/nfts/src/features/atomic_swap.rs b/frame/nfts/src/features/atomic_swap.rs index fc8d321d21094..1b83525818379 100644 --- a/frame/nfts/src/features/atomic_swap.rs +++ b/frame/nfts/src/features/atomic_swap.rs @@ -32,7 +32,7 @@ impl, I: 'static> Pallet { duration: ::BlockNumber, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoSwaps), + Self::is_pallet_feature_enabled(PalletFeature::Swaps), Error::::MethodDisabled ); ensure!(duration <= T::MaxDeadlineDuration::get(), Error::::WrongDuration); @@ -116,7 +116,7 @@ impl, I: 'static> Pallet { witness_price: Option>>, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoSwaps), + Self::is_pallet_feature_enabled(PalletFeature::Swaps), Error::::MethodDisabled ); diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index c5e906ce1b29e..3269edf5604f0 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -57,7 +57,7 @@ impl, I: 'static> Pallet { Ok((!settings.contains(setting), settings)) } - pub fn is_pallet_feature_disabled(feature: PalletFeature) -> bool { + pub fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { let features = T::Features::get(); return !features.0.contains(feature) } diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index ed9f351bceade..8d69e2b8d94c6 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -259,7 +259,7 @@ impl, I: 'static> Pallet { whitelisted_buyer: Option, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoTrading), + Self::is_pallet_feature_enabled(PalletFeature::Trading), Error::::MethodDisabled ); @@ -299,7 +299,7 @@ impl, I: 'static> Pallet { bid_price: ItemPrice, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoTrading), + Self::is_pallet_feature_enabled(PalletFeature::Trading), Error::::MethodDisabled ); diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 76a5664889ec6..06343305d8101 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -1023,7 +1023,7 @@ pub mod pallet { maybe_deadline: Option<::BlockNumber>, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoApprovals), + Self::is_pallet_feature_enabled(PalletFeature::Approvals), Error::::MethodDisabled ); let maybe_check: Option = T::ForceOrigin::try_origin(origin) @@ -1273,7 +1273,7 @@ pub mod pallet { value: BoundedVec, ) -> DispatchResult { ensure!( - Self::is_pallet_feature_disabled(PalletFeature::NoAttributes), + Self::is_pallet_feature_enabled(PalletFeature::Attributes), Error::::MethodDisabled ); let maybe_check_owner = T::ForceOrigin::try_origin(origin) diff --git a/frame/nfts/src/mock.rs b/frame/nfts/src/mock.rs index 431d05a762be9..bbd1625710500 100644 --- a/frame/nfts/src/mock.rs +++ b/frame/nfts/src/mock.rs @@ -85,7 +85,7 @@ impl pallet_balances::Config for Test { } parameter_types! { - pub storage Features: PalletFeatures = PalletFeatures::empty(); + pub storage Features: PalletFeatures = PalletFeatures::all_enabled(); } impl Config for Test { diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 3ed75af423102..8c528e66bf3bc 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -1723,8 +1723,8 @@ fn collection_locking_should_work() { #[test] fn pallet_level_feature_flags_should_work() { new_test_ext().execute_with(|| { - Features::set(&PalletFeatures( - PalletFeature::NoTrading | PalletFeature::NoApprovals | PalletFeature::NoAttributes, + Features::set(&PalletFeatures::disable( + PalletFeature::Trading | PalletFeature::Approvals | PalletFeature::Attributes, )); let user_id = 1; @@ -1741,7 +1741,7 @@ fn pallet_level_feature_flags_should_work() { default_item_config(), )); - // PalletFeature::NoTrading + // PalletFeature::Trading assert_noop!( Nfts::set_price(RuntimeOrigin::signed(user_id), collection_id, item_id, Some(1), None), Error::::MethodDisabled @@ -1751,13 +1751,13 @@ fn pallet_level_feature_flags_should_work() { Error::::MethodDisabled ); - // PalletFeature::NoApprovals + // PalletFeature::Approvals assert_noop!( Nfts::approve_transfer(RuntimeOrigin::signed(user_id), collection_id, item_id, 2, None), Error::::MethodDisabled ); - // PalletFeature::NoAttributes + // PalletFeature::Attributes assert_noop!( Nfts::set_attribute( RuntimeOrigin::signed(user_id), diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index dc4d76ff0ad46..757c3f8960fd9 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -235,16 +235,16 @@ impl_codec_bitflags!(ItemConfig, u64, ItemSetting); #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum PalletFeature { - /// Disallow trading operations. - NoTrading, - /// Disallow setting attributes. - NoAttributes, - /// Disallow transfer approvals. - NoApprovals, - /// Disallow atomic items swap. - NoSwaps, - /// Disallow public mints. - NoPublicMints, + /// Enable/disable trading operations. + Trading, + /// Allow/disallow setting attributes. + Attributes, + /// Allow/disallow transfer approvals. + Approvals, + /// Allow/disallow atomic items swap. + Swaps, + /// Allow/disallow public mints. + PublicMints, } /// Wrapper type for `BitFlags` that implements `Codec`. @@ -252,8 +252,11 @@ pub enum PalletFeature { pub struct PalletFeatures(pub BitFlags); impl PalletFeatures { - pub fn empty() -> Self { + pub fn all_enabled() -> Self { Self(BitFlags::EMPTY) } + pub fn disable(features: BitFlags) -> Self { + Self(features) + } } impl_codec_bitflags!(PalletFeatures, u64, PalletFeature); From ad980de55c6a245867541f104ce3ab8957b4d1cf Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 13:41:05 +0300 Subject: [PATCH 50/63] Move macros --- frame/nfts/src/features/mod.rs | 1 - frame/nfts/src/lib.rs | 1 + frame/nfts/src/{features => }/macros.rs | 0 frame/nfts/src/types.rs | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename frame/nfts/src/{features => }/macros.rs (100%) diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index 7bcb0caa200f2..685ea29443299 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -19,5 +19,4 @@ pub mod atomic_swap; pub mod buy_sell; pub mod freeze; pub mod lock; -pub mod macros; pub mod settings; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 06343305d8101..2b21a9c1ed97c 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -40,6 +40,7 @@ mod functions; mod impl_nonfungibles; mod types; +pub mod macros; pub mod weights; use codec::{Decode, Encode}; diff --git a/frame/nfts/src/features/macros.rs b/frame/nfts/src/macros.rs similarity index 100% rename from frame/nfts/src/features/macros.rs rename to frame/nfts/src/macros.rs diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 757c3f8960fd9..81a48582a3ddb 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -18,7 +18,7 @@ //! Various basic types for use in the Nfts pallet. use super::*; -use crate::features::macros::*; +use crate::macros::*; use codec::EncodeLike; use enumflags2::{bitflags, BitFlags}; use frame_support::{ From 8532179425061c531a6f7dc5b597f6eb7e9c270f Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 13:42:15 +0300 Subject: [PATCH 51/63] Change comments --- frame/nfts/src/features/freeze.rs | 6 +++--- frame/nfts/src/features/lock.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs index 264fdf77e6a73..9ea70a7850da2 100644 --- a/frame/nfts/src/features/freeze.rs +++ b/frame/nfts/src/features/freeze.rs @@ -18,9 +18,9 @@ use crate::*; use frame_support::pallet_prelude::*; -/// Freeze functions allow to make particular items non-transferable. -/// It's also possible to revert the setting back. -/// An origin must have a `Freezer` role in order to call those methods. +//! Freeze functions allow to make particular items non-transferable. +//! It's also possible to revert the setting back. +//! An origin must have a `Freezer` role in order to call those methods. impl, I: 'static> Pallet { pub fn do_freeze_item( origin: T::AccountId, diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index 9e5cb727cc081..f04614016af42 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -18,9 +18,9 @@ use crate::*; use frame_support::pallet_prelude::*; -/// Lock functions allow to lock collection/items metadata and attributes. -/// Additionally, it's possible to make all collection items non-transferable. -/// Those settings are irreversible. +//! Lock functions allow to lock collection/items metadata and attributes. +//! Additionally, it's possible to make all collection items non-transferable. +//! Those settings are irreversible. impl, I: 'static> Pallet { pub fn do_lock_collection( origin: T::AccountId, From fc09bbd9fd221a27e91c1920e8d70b887ae32105 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 13:43:38 +0300 Subject: [PATCH 52/63] Fmt --- frame/nfts/src/features/freeze.rs | 7 ++++--- frame/nfts/src/features/lock.rs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs index 9ea70a7850da2..26c0361e0f46a 100644 --- a/frame/nfts/src/features/freeze.rs +++ b/frame/nfts/src/features/freeze.rs @@ -15,12 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use frame_support::pallet_prelude::*; - //! Freeze functions allow to make particular items non-transferable. //! It's also possible to revert the setting back. //! An origin must have a `Freezer` role in order to call those methods. + +use crate::*; +use frame_support::pallet_prelude::*; + impl, I: 'static> Pallet { pub fn do_freeze_item( origin: T::AccountId, diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index f04614016af42..d45eb7e0ac842 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -15,12 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::*; -use frame_support::pallet_prelude::*; - //! Lock functions allow to lock collection/items metadata and attributes. //! Additionally, it's possible to make all collection items non-transferable. //! Those settings are irreversible. + +use crate::*; +use frame_support::pallet_prelude::*; + impl, I: 'static> Pallet { pub fn do_lock_collection( origin: T::AccountId, From e3f67ad7377fbe7a618cb37b4a935f126cc0997f Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 13:55:51 +0300 Subject: [PATCH 53/63] Refactor Incrementable --- frame/nfts/src/macros.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frame/nfts/src/macros.rs b/frame/nfts/src/macros.rs index d006e82bd2bea..07a8f3b9f9556 100644 --- a/frame/nfts/src/macros.rs +++ b/frame/nfts/src/macros.rs @@ -20,7 +20,9 @@ macro_rules! impl_incrementable { $( impl Incrementable for $type { fn increment(&self) -> Self { - self.saturating_add(1) + let mut val = self.clone(); + val.saturating_inc(); + val } fn initial_value() -> Self { From 1966a58b3d1430f1b6c18203f2adb1933260c288 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 14:03:56 +0300 Subject: [PATCH 54/63] Use pub(crate) for do_* functions --- frame/nfts/src/features/atomic_swap.rs | 6 +++--- frame/nfts/src/features/buy_sell.rs | 2 +- frame/nfts/src/features/freeze.rs | 4 ++-- frame/nfts/src/features/lock.rs | 4 ++-- frame/nfts/src/features/settings.rs | 10 +++++----- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/frame/nfts/src/features/atomic_swap.rs b/frame/nfts/src/features/atomic_swap.rs index 1b83525818379..bacaccdaedcbf 100644 --- a/frame/nfts/src/features/atomic_swap.rs +++ b/frame/nfts/src/features/atomic_swap.rs @@ -22,7 +22,7 @@ use frame_support::{ }; impl, I: 'static> Pallet { - pub fn do_create_swap( + pub(crate) fn do_create_swap( caller: T::AccountId, offered_collection_id: T::CollectionId, offered_item_id: T::ItemId, @@ -78,7 +78,7 @@ impl, I: 'static> Pallet { Ok(()) } - pub fn do_cancel_swap( + pub(crate) fn do_cancel_swap( caller: T::AccountId, offered_collection_id: T::CollectionId, offered_item_id: T::ItemId, @@ -107,7 +107,7 @@ impl, I: 'static> Pallet { Ok(()) } - pub fn do_claim_swap( + pub(crate) fn do_claim_swap( caller: T::AccountId, send_collection_id: T::CollectionId, send_item_id: T::ItemId, diff --git a/frame/nfts/src/features/buy_sell.rs b/frame/nfts/src/features/buy_sell.rs index 295d7fadfa8e6..c1e29057af9c9 100644 --- a/frame/nfts/src/features/buy_sell.rs +++ b/frame/nfts/src/features/buy_sell.rs @@ -22,7 +22,7 @@ use frame_support::{ }; impl, I: 'static> Pallet { - pub fn do_pay_tips( + pub(crate) fn do_pay_tips( sender: T::AccountId, tips: BoundedVec, T::MaxTips>, ) -> DispatchResult { diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs index 26c0361e0f46a..5badf5cfcdbe0 100644 --- a/frame/nfts/src/features/freeze.rs +++ b/frame/nfts/src/features/freeze.rs @@ -23,7 +23,7 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { - pub fn do_freeze_item( + pub(crate) fn do_freeze_item( origin: T::AccountId, collection: T::CollectionId, item: T::ItemId, @@ -42,7 +42,7 @@ impl, I: 'static> Pallet { Ok(()) } - pub fn do_thaw_item( + pub(crate) fn do_thaw_item( origin: T::AccountId, collection: T::CollectionId, item: T::ItemId, diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index d45eb7e0ac842..54f5d128175a4 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -23,7 +23,7 @@ use crate::*; use frame_support::pallet_prelude::*; impl, I: 'static> Pallet { - pub fn do_lock_collection( + pub(crate) fn do_lock_collection( origin: T::AccountId, collection: T::CollectionId, lock_config: CollectionConfig, @@ -54,7 +54,7 @@ impl, I: 'static> Pallet { }) } - pub fn do_lock_item( + pub(crate) fn do_lock_item( maybe_check_owner: Option, collection: T::CollectionId, item: T::ItemId, diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index 3269edf5604f0..bd011b5246cb3 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -23,7 +23,7 @@ use frame_support::pallet_prelude::*; /// For example, those settings allow to disable NFTs trading on a pallet level, or for a particular /// collection, or for a specific item. impl, I: 'static> Pallet { - pub fn get_collection_settings( + pub(crate) fn get_collection_settings( collection_id: &T::CollectionId, ) -> Result { let config = CollectionConfigOf::::get(&collection_id) @@ -31,7 +31,7 @@ impl, I: 'static> Pallet { Ok(config.values()) } - pub fn get_item_settings( + pub(crate) fn get_item_settings( collection_id: &T::CollectionId, item_id: &T::ItemId, ) -> Result { @@ -40,7 +40,7 @@ impl, I: 'static> Pallet { Ok(config.values()) } - pub fn is_collection_setting_disabled( + pub(crate) fn is_collection_setting_disabled( collection_id: &T::CollectionId, setting: CollectionSetting, ) -> Result<(bool, CollectionSettings), DispatchError> { @@ -48,7 +48,7 @@ impl, I: 'static> Pallet { Ok((!settings.contains(setting), settings)) } - pub fn is_item_setting_disabled( + pub(crate) fn is_item_setting_disabled( collection_id: &T::CollectionId, item_id: &T::ItemId, setting: ItemSetting, @@ -57,7 +57,7 @@ impl, I: 'static> Pallet { Ok((!settings.contains(setting), settings)) } - pub fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { + pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { let features = T::Features::get(); return !features.0.contains(feature) } From 39c84b475dff03868e95c6a1e7f1eab414c8e0fa Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 14:18:23 +0300 Subject: [PATCH 55/63] Update comments --- frame/nfts/src/lib.rs | 38 ++++++++++++++++---------------------- frame/nfts/src/types.rs | 11 ++++++++--- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 2b21a9c1ed97c..e25da447e509b 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -176,15 +176,9 @@ pub mod pallet { type WeightInfo: WeightInfo; } - pub type ApprovalsOf = BoundedBTreeMap< - ::AccountId, - Option<::BlockNumber>, - >::ApprovalsLimit, - >; - + /// Details of a collection. #[pallet::storage] #[pallet::storage_prefix = "Class"] - /// Details of a collection. pub(super) type Collection, I: 'static = ()> = StorageMap< _, Blake2_128Concat, @@ -192,14 +186,14 @@ pub mod pallet { CollectionDetails>, >; - #[pallet::storage] /// The collection, if any, of which an account is willing to take ownership. + #[pallet::storage] pub(super) type OwnershipAcceptance, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::AccountId, T::CollectionId>; - #[pallet::storage] /// The items held by any given account; set out this way so that items owned by a single /// account can be enumerated. + #[pallet::storage] pub(super) type Account, I: 'static = ()> = StorageNMap< _, ( @@ -211,10 +205,10 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] - #[pallet::storage_prefix = "ClassAccount"] /// The collections owned by any given account; set out this way so that collections owned by /// a single account can be enumerated. + #[pallet::storage] + #[pallet::storage_prefix = "ClassAccount"] pub(super) type CollectionAccount, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -225,9 +219,9 @@ pub mod pallet { OptionQuery, >; + /// The items in existence and their ownership details. #[pallet::storage] #[pallet::storage_prefix = "Asset"] - /// The items in existence and their ownership details. pub(super) type Item, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -238,9 +232,9 @@ pub mod pallet { OptionQuery, >; + /// Metadata of a collection. #[pallet::storage] #[pallet::storage_prefix = "ClassMetadataOf"] - /// Metadata of a collection. pub(super) type CollectionMetadataOf, I: 'static = ()> = StorageMap< _, Blake2_128Concat, @@ -249,9 +243,9 @@ pub mod pallet { OptionQuery, >; + /// Metadata of an item. #[pallet::storage] #[pallet::storage_prefix = "InstanceMetadataOf"] - /// Metadata of an item. pub(super) type ItemMetadataOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -262,8 +256,8 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// Attributes of a collection. + #[pallet::storage] pub(super) type Attribute, I: 'static = ()> = StorageNMap< _, ( @@ -275,8 +269,8 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// Price of an asset instance. + #[pallet::storage] pub(super) type ItemPriceOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -287,19 +281,19 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// Keeps track of the number of items a collection might have. + #[pallet::storage] pub(super) type CollectionMaxSupply, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::CollectionId, u32, OptionQuery>; - #[pallet::storage] /// Stores the `CollectionId` that is going to be used for the next collection. /// This gets incremented by 1 whenever a new collection is created. + #[pallet::storage] pub(super) type NextCollectionId, I: 'static = ()> = StorageValue<_, T::CollectionId, OptionQuery>; - #[pallet::storage] /// Handles all the pending swaps. + #[pallet::storage] pub(super) type PendingSwapOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -315,13 +309,13 @@ pub mod pallet { OptionQuery, >; - #[pallet::storage] /// Config of a collection. + #[pallet::storage] pub(super) type CollectionConfigOf, I: 'static = ()> = StorageMap<_, Blake2_128Concat, T::CollectionId, CollectionConfig, OptionQuery>; - #[pallet::storage] /// Config of an item. + #[pallet::storage] pub(super) type ItemConfigOf, I: 'static = ()> = StorageDoubleMap< _, Blake2_128Concat, @@ -498,7 +492,7 @@ pub mod pallet { ApprovalExpired, /// The owner turned out to be different to what was expected. WrongOwner, - /// Invalid witness data given. + /// The witness data given does not match the current state of the chain. BadWitness, /// Collection ID is already taken. CollectionIdInUse, diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 81a48582a3ddb..6d47cbfa4a1ef 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -31,6 +31,11 @@ pub(super) type DepositBalanceOf = <>::Currency as Currency<::AccountId>>::Balance; pub(super) type CollectionDetailsFor = CollectionDetails<::AccountId, DepositBalanceOf>; +pub(super) type ApprovalsOf = BoundedBTreeMap< + ::AccountId, + Option<::BlockNumber>, + >::ApprovalsLimit, +>; pub(super) type ItemDetailsFor = ItemDetails<::AccountId, DepositBalanceOf, ApprovalsOf>; pub(super) type BalanceOf = @@ -172,7 +177,7 @@ pub struct PriceWithDirection { pub(super) direction: PriceDirection, } -// Support for up to 64 user-enabled features on a collection. +/// Support for up to 64 user-enabled features on a collection. #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -202,7 +207,7 @@ impl CollectionConfig { } impl_codec_bitflags!(CollectionConfig, u64, CollectionSetting); -// Support for up to 64 user-enabled features on an item. +/// Support for up to 64 user-enabled features on an item. #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] @@ -230,7 +235,7 @@ impl ItemConfig { } impl_codec_bitflags!(ItemConfig, u64, ItemSetting); -// Support for up to 64 system-enabled features on a collection. +/// Support for up to 64 system-enabled features on a collection. #[bitflags] #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] From 71ff09eeb38dca4e3f0a67e7f46c0b3ea02037b3 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 17:07:12 +0300 Subject: [PATCH 56/63] Refactor freeze and lock functions --- frame/nfts/src/benchmarking.rs | 14 +++---- frame/nfts/src/features/freeze.rs | 63 ------------------------------- frame/nfts/src/features/lock.rs | 46 +++++++++++++++++++--- frame/nfts/src/features/mod.rs | 1 - frame/nfts/src/lib.rs | 52 ++++++++++++++----------- frame/nfts/src/tests.rs | 36 +++++++++++------- frame/nfts/src/weights.rs | 18 ++++----- 7 files changed, 108 insertions(+), 122 deletions(-) delete mode 100644 frame/nfts/src/features/freeze.rs diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 9715977b719d8..382a832c7c278 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -211,25 +211,25 @@ benchmarks_instance_pallet! { assert_last_event::(Event::Redeposited { collection, successful_items: items }.into()); } - freeze { + lock_item_transfer { let (collection, caller, caller_lookup) = create_collection::(); let (item, ..) = mint_item::(0); }: _(SystemOrigin::Signed(caller.clone()), T::Helper::collection(0), T::Helper::item(0)) verify { - assert_last_event::(Event::Frozen { collection: T::Helper::collection(0), item: T::Helper::item(0) }.into()); + assert_last_event::(Event::ItemTransferLocked { collection: T::Helper::collection(0), item: T::Helper::item(0) }.into()); } - thaw { + unlock_item_transfer { let (collection, caller, caller_lookup) = create_collection::(); let (item, ..) = mint_item::(0); - Nfts::::freeze( + Nfts::::lock_item_transfer( SystemOrigin::Signed(caller.clone()).into(), collection, item, )?; }: _(SystemOrigin::Signed(caller.clone()), collection, item) verify { - assert_last_event::(Event::Thawed { collection, item }.into()); + assert_last_event::(Event::ItemTransferUnlocked { collection, item }.into()); } lock_collection { @@ -287,14 +287,14 @@ benchmarks_instance_pallet! { assert_last_event::(Event::CollectionStatusChanged { collection }.into()); } - lock_item { + lock_item_properties { let (collection, caller, caller_lookup) = create_collection::(); let (item, ..) = mint_item::(0); let lock_metadata = true; let lock_attributes = true; }: _(SystemOrigin::Signed(caller), collection, item, lock_metadata, lock_attributes) verify { - assert_last_event::(Event::ItemLocked { collection, item, lock_metadata, lock_attributes }.into()); + assert_last_event::(Event::ItemPropertiesLocked { collection, item, lock_metadata, lock_attributes }.into()); } set_attribute { diff --git a/frame/nfts/src/features/freeze.rs b/frame/nfts/src/features/freeze.rs deleted file mode 100644 index 5badf5cfcdbe0..0000000000000 --- a/frame/nfts/src/features/freeze.rs +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Freeze functions allow to make particular items non-transferable. -//! It's also possible to revert the setting back. -//! An origin must have a `Freezer` role in order to call those methods. - -use crate::*; -use frame_support::pallet_prelude::*; - -impl, I: 'static> Pallet { - pub(crate) fn do_freeze_item( - origin: T::AccountId, - collection: T::CollectionId, - item: T::ItemId, - ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - let mut settings = Self::get_item_settings(&collection, &item)?; - if !settings.contains(ItemSetting::NonTransferable) { - settings.insert(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Frozen { collection, item }); - Ok(()) - } - - pub(crate) fn do_thaw_item( - origin: T::AccountId, - collection: T::CollectionId, - item: T::ItemId, - ) -> DispatchResult { - let collection_details = - Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - ensure!(collection_details.freezer == origin, Error::::NoPermission); - - let mut settings = Self::get_item_settings(&collection, &item)?; - if settings.contains(ItemSetting::NonTransferable) { - settings.remove(ItemSetting::NonTransferable); - } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); - - Self::deposit_event(Event::::Thawed { collection, item }); - Ok(()) - } -} diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index 54f5d128175a4..99188e9cc6804 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -15,10 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Lock functions allow to lock collection/items metadata and attributes. -//! Additionally, it's possible to make all collection items non-transferable. -//! Those settings are irreversible. - use crate::*; use frame_support::pallet_prelude::*; @@ -54,7 +50,45 @@ impl, I: 'static> Pallet { }) } - pub(crate) fn do_lock_item( + pub(crate) fn do_lock_item_transfer( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + + let mut settings = Self::get_item_settings(&collection, &item)?; + if !settings.contains(ItemSetting::NonTransferable) { + settings.insert(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::ItemTransferLocked { collection, item }); + Ok(()) + } + + pub(crate) fn do_unlock_item_transfer( + origin: T::AccountId, + collection: T::CollectionId, + item: T::ItemId, + ) -> DispatchResult { + let collection_details = + Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; + ensure!(collection_details.freezer == origin, Error::::NoPermission); + + let mut settings = Self::get_item_settings(&collection, &item)?; + if settings.contains(ItemSetting::NonTransferable) { + settings.remove(ItemSetting::NonTransferable); + } + ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + + Self::deposit_event(Event::::ItemTransferUnlocked { collection, item }); + Ok(()) + } + + pub(crate) fn do_lock_item_properties( maybe_check_owner: Option, collection: T::CollectionId, item: T::ItemId, @@ -81,7 +115,7 @@ impl, I: 'static> Pallet { config.0 = settings; - Self::deposit_event(Event::::ItemLocked { + Self::deposit_event(Event::::ItemPropertiesLocked { collection, item, lock_metadata, diff --git a/frame/nfts/src/features/mod.rs b/frame/nfts/src/features/mod.rs index 685ea29443299..47e5816bc953c 100644 --- a/frame/nfts/src/features/mod.rs +++ b/frame/nfts/src/features/mod.rs @@ -17,6 +17,5 @@ pub mod atomic_swap; pub mod buy_sell; -pub mod freeze; pub mod lock; pub mod settings; diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index e25da447e509b..35a7f02f481aa 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -346,12 +346,12 @@ pub mod pallet { }, /// An `item` was destroyed. Burned { collection: T::CollectionId, item: T::ItemId, owner: T::AccountId }, - /// Some `item` was frozen. - Frozen { collection: T::CollectionId, item: T::ItemId }, - /// Some `item` was thawed. - Thawed { collection: T::CollectionId, item: T::ItemId }, - /// Some `item` was locked. - ItemLocked { + /// An `item` became non-transferable. + ItemTransferLocked { collection: T::CollectionId, item: T::ItemId }, + /// An `item` became transferable. + ItemTransferUnlocked { collection: T::CollectionId, item: T::ItemId }, + /// `item` metadata or attributes were locked. + ItemPropertiesLocked { collection: T::CollectionId, item: T::ItemId, lock_metadata: bool, @@ -508,7 +508,7 @@ pub mod pallet { Unapproved, /// The named owner has not signed ownership of the collection is acceptable. Unaccepted, - /// The item is locked. + /// The item is locked (non-transferable). ItemLocked, /// Item's attributes are locked. LockedItemAttributes, @@ -854,40 +854,40 @@ pub mod pallet { /// /// Origin must be Signed and the sender should be the Freezer of the `collection`. /// - /// - `collection`: The collection of the item to be frozen. - /// - `item`: The item of the item to be frozen. + /// - `collection`: The collection of the item to be changed. + /// - `item`: The item to become non-transferable. /// - /// Emits `Frozen`. + /// Emits `ItemTransferLocked`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::freeze())] - pub fn freeze( + #[pallet::weight(T::WeightInfo::lock_item_transfer())] + pub fn lock_item_transfer( origin: OriginFor, collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - Self::do_freeze_item(origin, collection, item) + Self::do_lock_item_transfer(origin, collection, item) } /// Re-allow unprivileged transfer of an item. /// /// Origin must be Signed and the sender should be the Freezer of the `collection`. /// - /// - `collection`: The collection of the item to be thawed. - /// - `item`: The item of the item to be thawed. + /// - `collection`: The collection of the item to be changed. + /// - `item`: The item to become transferable. /// - /// Emits `Thawed`. + /// Emits `ItemTransferUnlocked`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::thaw())] - pub fn thaw( + #[pallet::weight(T::WeightInfo::unlock_item_transfer())] + pub fn unlock_item_transfer( origin: OriginFor, collection: T::CollectionId, item: T::ItemId, ) -> DispatchResult { let origin = ensure_signed(origin)?; - Self::do_thaw_item(origin, collection, item) + Self::do_unlock_item_transfer(origin, collection, item) } /// Disallows specified settings for the whole collection. @@ -1224,11 +1224,11 @@ pub mod pallet { /// - `lock_config`: The config with the settings to be locked. /// /// Note: when the metadata or attributes are locked, it won't be possible the unlock them. - /// Emits `ItemLocked`. + /// Emits `ItemPropertiesLocked`. /// /// Weight: `O(1)` - #[pallet::weight(T::WeightInfo::lock_item())] - pub fn lock_item( + #[pallet::weight(T::WeightInfo::lock_item_properties())] + pub fn lock_item_properties( origin: OriginFor, collection: T::CollectionId, item: T::ItemId, @@ -1239,7 +1239,13 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - Self::do_lock_item(maybe_check_owner, collection, item, lock_metadata, lock_attributes) + Self::do_lock_item_properties( + maybe_check_owner, + collection, + item, + lock_metadata, + lock_attributes, + ) } /// Set an attribute for a collection or item. diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 8c528e66bf3bc..362adfc7c5840 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -225,14 +225,14 @@ fn transfer_should_work() { } #[test] -fn freezing_should_work() { +fn locking_transfer_should_work() { new_test_ext().execute_with(|| { assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); - assert_ok!(Nfts::freeze(RuntimeOrigin::signed(1), 0, 42)); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(1), 0, 42)); assert_noop!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), Error::::ItemLocked); - assert_ok!(Nfts::thaw(RuntimeOrigin::signed(1), 0, 42)); + assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(1), 0, 42)); assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(1), 0, @@ -272,8 +272,14 @@ fn origin_guards_should_work() { Nfts::set_team(RuntimeOrigin::signed(2), 0, 2, 2, 2), Error::::NoPermission ); - assert_noop!(Nfts::freeze(RuntimeOrigin::signed(2), 0, 42), Error::::NoPermission); - assert_noop!(Nfts::thaw(RuntimeOrigin::signed(2), 0, 42), Error::::NoPermission); + assert_noop!( + Nfts::lock_item_transfer(RuntimeOrigin::signed(2), 0, 42), + Error::::NoPermission + ); + assert_noop!( + Nfts::unlock_item_transfer(RuntimeOrigin::signed(2), 0, 42), + Error::::NoPermission + ); assert_noop!( Nfts::mint(RuntimeOrigin::signed(2), 0, 69, 2, default_item_config()), Error::::NoPermission @@ -342,8 +348,8 @@ fn set_team_should_work() { assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); assert_ok!(Nfts::mint(RuntimeOrigin::signed(2), 0, 42, 2, default_item_config())); - assert_ok!(Nfts::freeze(RuntimeOrigin::signed(4), 0, 42)); - assert_ok!(Nfts::thaw(RuntimeOrigin::signed(4), 0, 42)); + assert_ok!(Nfts::lock_item_transfer(RuntimeOrigin::signed(4), 0, 42)); + assert_ok!(Nfts::unlock_item_transfer(RuntimeOrigin::signed(4), 0, 42)); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(3), 0, 42, 3)); assert_ok!(Nfts::burn(RuntimeOrigin::signed(3), 0, 42, None)); }); @@ -456,7 +462,7 @@ fn set_item_metadata_should_work() { // Can't set or clear metadata once frozen assert_ok!(Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15])); - assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 42, true, false)); + assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 42, true, false)); assert_noop!( Nfts::set_metadata(RuntimeOrigin::signed(1), 0, 42, bvec![0u8; 15]), Error::::LockedItemMetadata, @@ -560,7 +566,7 @@ fn set_attribute_should_respect_lock() { assert_noop!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0]), e); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1])); - assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 0, false, true)); + assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 0, false, true)); let e = Error::::LockedItemAttributes; assert_noop!( Nfts::set_attribute(RuntimeOrigin::signed(1), 0, Some(0), bvec![0], bvec![1]), @@ -584,7 +590,7 @@ fn preserve_config_for_frozen_items() { assert!(!ItemConfigOf::::contains_key(0, 1)); // lock the item and ensure the config stays unchanged - assert_ok!(Nfts::lock_item(RuntimeOrigin::signed(1), 0, 0, true, true)); + assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 0, true, true)); let expect_config = ItemConfig(ItemSetting::LockedAttributes | ItemSetting::LockedMetadata); let config = ItemConfigOf::::get(0, 0).unwrap(); @@ -1232,7 +1238,7 @@ fn buy_item_should_work() { Error::::ItemsNotTransferable ); - // un-freeze the collection + // unlock the collection assert_ok!(Nfts::force_collection_status( RuntimeOrigin::root(), collection_id, @@ -1243,8 +1249,12 @@ fn buy_item_should_work() { CollectionConfig::empty(), )); - // freeze the item - assert_ok!(Nfts::freeze(RuntimeOrigin::signed(user_1), collection_id, item_3)); + // lock the transfer + assert_ok!(Nfts::lock_item_transfer( + RuntimeOrigin::signed(user_1), + collection_id, + item_3 + )); let buy_item_call = mock::RuntimeCall::Nfts(crate::Call::::buy_item { collection: collection_id, diff --git a/frame/nfts/src/weights.rs b/frame/nfts/src/weights.rs index 8d74683834571..5f6ee43a09ffe 100644 --- a/frame/nfts/src/weights.rs +++ b/frame/nfts/src/weights.rs @@ -53,13 +53,13 @@ pub trait WeightInfo { fn burn() -> Weight; fn transfer() -> Weight; fn redeposit(i: u32, ) -> Weight; - fn freeze() -> Weight; - fn thaw() -> Weight; + fn lock_item_transfer() -> Weight; + fn unlock_item_transfer() -> Weight; fn lock_collection() -> Weight; fn transfer_ownership() -> Weight; fn set_team() -> Weight; fn force_collection_status() -> Weight; - fn lock_item() -> Weight; + fn lock_item_properties() -> Weight; fn set_attribute() -> Weight; fn clear_attribute() -> Weight; fn set_metadata() -> Weight; @@ -175,14 +175,14 @@ impl WeightInfo for SubstrateWeight { } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn freeze() -> Weight { + fn lock_item_transfer() -> Weight { Weight::from_ref_time(28_194_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn thaw() -> Weight { + fn unlock_item_transfer() -> Weight { Weight::from_ref_time(28_821_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) @@ -218,7 +218,7 @@ impl WeightInfo for SubstrateWeight { } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn lock_item() -> Weight { + fn lock_item_properties() -> Weight { Weight::from_ref_time(27_377_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) @@ -457,14 +457,14 @@ impl WeightInfo for () { } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn freeze() -> Weight { + fn lock_item_transfer() -> Weight { Weight::from_ref_time(28_194_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn thaw() -> Weight { + fn unlock_item_transfer() -> Weight { Weight::from_ref_time(28_821_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) @@ -500,7 +500,7 @@ impl WeightInfo for () { } // Storage: Nfts Class (r:1 w:0) // Storage: Nfts ItemConfigOf (r:1 w:1) - fn lock_item() -> Weight { + fn lock_item_properties() -> Weight { Weight::from_ref_time(27_377_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) From e3778adaf9e4c14a84e6324ad7268b58ad29055a Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 19:09:15 +0300 Subject: [PATCH 57/63] Rework Collection config and Item confg api --- frame/nfts/src/benchmarking.rs | 20 ++--- frame/nfts/src/features/lock.rs | 39 ++++------ frame/nfts/src/features/settings.rs | 29 ++----- frame/nfts/src/functions.rs | 51 +++++++------ frame/nfts/src/impl_nonfungibles.rs | 13 +--- frame/nfts/src/lib.rs | 83 ++++++++++---------- frame/nfts/src/tests.rs | 114 ++++++++++++++++++---------- frame/nfts/src/types.rs | 69 ++++++++++++----- 8 files changed, 229 insertions(+), 189 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 382a832c7c278..11950395b7ad0 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -46,7 +46,7 @@ fn create_collection, I: 'static>( assert_ok!(Nfts::::force_create( SystemOrigin::Root.into(), caller_lookup.clone(), - CollectionConfig::empty() + CollectionConfig::all_settings_enabled() )); (collection, caller, caller_lookup) } @@ -79,7 +79,7 @@ fn mint_item, I: 'static>( T::Helper::collection(0), item, caller_lookup.clone(), - ItemConfig::empty(), + ItemConfig::all_settings_enabled(), )); (item, caller, caller_lookup) } @@ -136,7 +136,7 @@ benchmarks_instance_pallet! { whitelist_account!(caller); let admin = T::Lookup::unlookup(caller.clone()); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); - let call = Call::::create { admin, config: CollectionConfig::empty() }; + let call = Call::::create { admin, config: CollectionConfig::all_settings_enabled() }; }: { call.dispatch_bypass_filter(origin)? } verify { assert_last_event::(Event::Created { collection: T::Helper::collection(0), creator: caller.clone(), owner: caller }.into()); @@ -145,7 +145,7 @@ benchmarks_instance_pallet! { force_create { let caller: T::AccountId = whitelisted_caller(); let caller_lookup = T::Lookup::unlookup(caller.clone()); - }: _(SystemOrigin::Root, caller_lookup, CollectionConfig::empty()) + }: _(SystemOrigin::Root, caller_lookup, CollectionConfig::all_settings_enabled()) verify { assert_last_event::(Event::ForceCreated { collection: T::Helper::collection(0), owner: caller }.into()); } @@ -169,7 +169,7 @@ benchmarks_instance_pallet! { mint { let (collection, caller, caller_lookup) = create_collection::(); let item = T::Helper::item(0); - }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, ItemConfig::empty()) + }: _(SystemOrigin::Signed(caller.clone()), collection, item, caller_lookup, ItemConfig::all_settings_enabled()) verify { assert_last_event::(Event::Issued { collection, item, owner: caller }.into()); } @@ -204,7 +204,7 @@ benchmarks_instance_pallet! { caller_lookup.clone(), caller_lookup.clone(), caller_lookup, - CollectionConfig(CollectionSetting::FreeHolding.into()), + CollectionConfig(CollectionSetting::RequiredDeposit.into()), )?; }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone()) verify { @@ -235,9 +235,9 @@ benchmarks_instance_pallet! { lock_collection { let (collection, caller, caller_lookup) = create_collection::(); let lock_config = CollectionConfig( - CollectionSetting::NonTransferableItems | - CollectionSetting::LockedMetadata | - CollectionSetting::LockedAttributes, + CollectionSetting::TransferableItems | + CollectionSetting::UnlockedMetadata | + CollectionSetting::UnlockedAttributes, ); }: _(SystemOrigin::Signed(caller.clone()), collection, lock_config) verify { @@ -280,7 +280,7 @@ benchmarks_instance_pallet! { issuer: caller_lookup.clone(), admin: caller_lookup.clone(), freezer: caller_lookup, - config: CollectionConfig(CollectionSetting::FreeHolding.into()), + config: CollectionConfig(CollectionSetting::RequiredDeposit.into()), }; }: { call.dispatch_bypass_filter(origin)? } verify { diff --git a/frame/nfts/src/features/lock.rs b/frame/nfts/src/features/lock.rs index 99188e9cc6804..0a5fecc1d6224 100644 --- a/frame/nfts/src/features/lock.rs +++ b/frame/nfts/src/features/lock.rs @@ -30,21 +30,17 @@ impl, I: 'static> Pallet { CollectionConfigOf::::try_mutate(collection, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::NoConfig)?; - let mut settings = config.values(); - let lock_settings = lock_config.values(); - if lock_settings.contains(CollectionSetting::NonTransferableItems) { - settings.insert(CollectionSetting::NonTransferableItems); + if lock_config.has_disabled_setting(CollectionSetting::TransferableItems) { + config.disable_setting(CollectionSetting::TransferableItems); } - if lock_settings.contains(CollectionSetting::LockedMetadata) { - settings.insert(CollectionSetting::LockedMetadata); + if lock_config.has_disabled_setting(CollectionSetting::UnlockedMetadata) { + config.disable_setting(CollectionSetting::UnlockedMetadata); } - if lock_settings.contains(CollectionSetting::LockedAttributes) { - settings.insert(CollectionSetting::LockedAttributes); + if lock_config.has_disabled_setting(CollectionSetting::UnlockedAttributes) { + config.disable_setting(CollectionSetting::UnlockedAttributes); } - config.0 = settings; - Self::deposit_event(Event::::CollectionLocked { collection }); Ok(()) }) @@ -59,11 +55,11 @@ impl, I: 'static> Pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.freezer == origin, Error::::NoPermission); - let mut settings = Self::get_item_settings(&collection, &item)?; - if !settings.contains(ItemSetting::NonTransferable) { - settings.insert(ItemSetting::NonTransferable); + let mut config = Self::get_item_config(&collection, &item)?; + if !config.has_disabled_setting(ItemSetting::Transferable) { + config.disable_setting(ItemSetting::Transferable); } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + ItemConfigOf::::insert(&collection, &item, config); Self::deposit_event(Event::::ItemTransferLocked { collection, item }); Ok(()) @@ -78,11 +74,11 @@ impl, I: 'static> Pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.freezer == origin, Error::::NoPermission); - let mut settings = Self::get_item_settings(&collection, &item)?; - if settings.contains(ItemSetting::NonTransferable) { - settings.remove(ItemSetting::NonTransferable); + let mut config = Self::get_item_config(&collection, &item)?; + if config.has_disabled_setting(ItemSetting::Transferable) { + config.enable_setting(ItemSetting::Transferable); } - ItemConfigOf::::insert(&collection, &item, ItemConfig(settings)); + ItemConfigOf::::insert(&collection, &item, config); Self::deposit_event(Event::::ItemTransferUnlocked { collection, item }); Ok(()) @@ -104,17 +100,14 @@ impl, I: 'static> Pallet { ItemConfigOf::::try_mutate(collection, item, |maybe_config| { let config = maybe_config.as_mut().ok_or(Error::::UnknownItem)?; - let mut settings = config.values(); if lock_metadata { - settings.insert(ItemSetting::LockedMetadata); + config.disable_setting(ItemSetting::UnlockedMetadata); } if lock_attributes { - settings.insert(ItemSetting::LockedAttributes); + config.disable_setting(ItemSetting::UnlockedAttributes); } - config.0 = settings; - Self::deposit_event(Event::::ItemPropertiesLocked { collection, item, diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index bd011b5246cb3..a66f7aca6dd17 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -23,38 +23,21 @@ use frame_support::pallet_prelude::*; /// For example, those settings allow to disable NFTs trading on a pallet level, or for a particular /// collection, or for a specific item. impl, I: 'static> Pallet { - pub(crate) fn get_collection_settings( + pub(crate) fn get_collection_config( collection_id: &T::CollectionId, - ) -> Result { + ) -> Result { let config = CollectionConfigOf::::get(&collection_id) .ok_or(Error::::UnknownCollection)?; - Ok(config.values()) + Ok(config) } - pub(crate) fn get_item_settings( + pub(crate) fn get_item_config( collection_id: &T::CollectionId, item_id: &T::ItemId, - ) -> Result { + ) -> Result { let config = ItemConfigOf::::get(&collection_id, &item_id) .ok_or(Error::::UnknownItem)?; - Ok(config.values()) - } - - pub(crate) fn is_collection_setting_disabled( - collection_id: &T::CollectionId, - setting: CollectionSetting, - ) -> Result<(bool, CollectionSettings), DispatchError> { - let settings = Self::get_collection_settings(&collection_id)?; - Ok((!settings.contains(setting), settings)) - } - - pub(crate) fn is_item_setting_disabled( - collection_id: &T::CollectionId, - item_id: &T::ItemId, - setting: ItemSetting, - ) -> Result<(bool, ItemSettings), DispatchError> { - let settings = Self::get_item_settings(&collection_id, &item_id)?; - Ok((!settings.contains(setting), settings)) + Ok(config) } pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 8d69e2b8d94c6..e52fc1d9a27e8 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -38,15 +38,17 @@ impl, I: 'static> Pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(!T::Locker::is_locked(collection, item), Error::::ItemLocked); - let (action_allowed, _) = Self::is_collection_setting_disabled( - &collection, - CollectionSetting::NonTransferableItems, - )?; - ensure!(action_allowed, Error::::ItemsNotTransferable); + let collection_config = Self::get_collection_config(&collection)?; + ensure!( + collection_config.is_setting_enabled(CollectionSetting::TransferableItems), + Error::::ItemsNotTransferable + ); - let (action_allowed, _) = - Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; - ensure!(action_allowed, Error::::ItemLocked); + let item_config = Self::get_item_config(&collection, &item)?; + ensure!( + item_config.is_setting_enabled(ItemSetting::Transferable), + Error::::ItemLocked + ); let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; @@ -184,11 +186,12 @@ impl, I: 'static> Pallet { collection_details.items.checked_add(1).ok_or(ArithmeticError::Overflow)?; collection_details.items = items; - let settings = Self::get_collection_settings(&collection)?; - - let deposit = match settings.contains(CollectionSetting::FreeHolding) { - true => Zero::zero(), - false => T::ItemDeposit::get(), + let collection_config = Self::get_collection_config(&collection)?; + let deposit = match collection_config + .is_setting_enabled(CollectionSetting::RequiredDeposit) + { + true => T::ItemDeposit::get(), + false => Zero::zero(), }; T::Currency::reserve(&collection_details.owner, deposit)?; collection_details.total_deposit += deposit; @@ -242,8 +245,8 @@ impl, I: 'static> Pallet { // NOTE: if item's settings are not empty (e.g. item's metadata is locked) // then we keep the record and don't remove it - let settings = Self::get_item_settings(&collection, &item)?; - if settings.is_empty() { + let config = Self::get_item_config(&collection, &item)?; + if !config.has_disabled_settings() { ItemConfigOf::::remove(&collection, &item); } @@ -266,15 +269,17 @@ impl, I: 'static> Pallet { let details = Item::::get(&collection, &item).ok_or(Error::::UnknownItem)?; ensure!(details.owner == sender, Error::::NoPermission); - let (action_allowed, _) = Self::is_collection_setting_disabled( - &collection, - CollectionSetting::NonTransferableItems, - )?; - ensure!(action_allowed, Error::::ItemsNotTransferable); + let collection_config = Self::get_collection_config(&collection)?; + ensure!( + collection_config.is_setting_enabled(CollectionSetting::TransferableItems), + Error::::ItemsNotTransferable + ); - let (action_allowed, _) = - Self::is_item_setting_disabled(&collection, &item, ItemSetting::NonTransferable)?; - ensure!(action_allowed, Error::::ItemLocked); + let item_config = Self::get_item_config(&collection, &item)?; + ensure!( + item_config.is_setting_enabled(ItemSetting::Transferable), + Error::::ItemLocked + ); if let Some(ref price) = price { ItemPriceOf::::insert(&collection, &item, (price, whitelisted_buyer.clone())); diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 45e544d460d2f..3d9ac339dd653 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -83,8 +83,8 @@ impl, I: 'static> Inspect<::AccountId> for Palle ItemConfigOf::::get(collection, item), ) { (Some(cc), Some(ic)) - if !cc.values().contains(CollectionSetting::NonTransferableItems) && - !ic.values().contains(ItemSetting::NonTransferable) => + if cc.is_setting_enabled(CollectionSetting::TransferableItems) && + ic.is_setting_enabled(ItemSetting::Transferable) => true, _ => false, } @@ -99,18 +99,13 @@ impl, I: 'static> Create<::AccountId, Collection collection: &Self::CollectionId, who: &T::AccountId, admin: &T::AccountId, - settings: &CollectionSettings, + disabled_settings: &CollectionSettings, ) -> DispatchResult { - let mut settings = *settings; - // FreeHolding could be set by calling the force_create() only - if settings.contains(CollectionSetting::FreeHolding) { - settings.remove(CollectionSetting::FreeHolding); - } Self::do_create_collection( *collection, who.clone(), admin.clone(), - CollectionConfig(settings), + CollectionConfig::disable_settings(*disabled_settings), T::CollectionDeposit::get(), Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, ) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 35a7f02f481aa..154e7eba3fcf9 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -587,12 +587,11 @@ pub mod pallet { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; let admin = T::Lookup::lookup(admin)?; - let mut settings = config.values(); - // FreeHolding could be set by calling the force_create() only - if settings.contains(CollectionSetting::FreeHolding) { - settings.remove(CollectionSetting::FreeHolding); + let mut config = config.clone(); + // RequiredDeposit could be skipped by calling the force_create() only + if !config.is_setting_enabled(CollectionSetting::RequiredDeposit) { + config.enable_setting(CollectionSetting::RequiredDeposit); } - let config = CollectionConfig(settings); Self::do_create_collection( collection, @@ -810,10 +809,10 @@ pub mod pallet { Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; ensure!(collection_details.owner == origin, Error::::NoPermission); - let settings = Self::get_collection_settings(&collection)?; - let deposit = match settings.contains(CollectionSetting::FreeHolding) { - true => Zero::zero(), - false => T::ItemDeposit::get(), + let config = Self::get_collection_config(&collection)?; + let deposit = match config.is_setting_enabled(CollectionSetting::RequiredDeposit) { + true => T::ItemDeposit::get(), + false => Zero::zero(), }; let mut successful = Vec::with_capacity(items.len()); @@ -1032,11 +1031,11 @@ pub mod pallet { let mut details = Item::::get(&collection, &item).ok_or(Error::::UnknownCollection)?; - let (action_allowed, _) = Self::is_collection_setting_disabled( - &collection, - CollectionSetting::NonTransferableItems, - )?; - ensure!(action_allowed, Error::::ItemsNotTransferable); + let collection_config = Self::get_collection_config(&collection)?; + ensure!( + collection_config.is_setting_enabled(CollectionSetting::TransferableItems), + Error::::ItemsNotTransferable + ); if let Some(check) = maybe_check { let permitted = check == collection_details.admin || check == details.owner; @@ -1288,17 +1287,17 @@ pub mod pallet { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } - let collection_settings = Self::get_collection_settings(&collection)?; + let collection_config = Self::get_collection_config(&collection)?; match maybe_item { None => { ensure!( - !collection_settings.contains(CollectionSetting::LockedAttributes), + collection_config.is_setting_enabled(CollectionSetting::UnlockedAttributes), Error::::LockedCollectionAttributes ) }, Some(item) => { - let maybe_is_locked = Self::get_item_settings(&collection, &item) - .map(|v| v.contains(ItemSetting::LockedAttributes))?; + let maybe_is_locked = Self::get_item_config(&collection, &item) + .map(|c| c.has_disabled_setting(ItemSetting::UnlockedAttributes))?; ensure!(!maybe_is_locked, Error::::LockedItemAttributes); }, }; @@ -1310,7 +1309,7 @@ pub mod pallet { let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !collection_settings.contains(CollectionSetting::FreeHolding) && + if collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() @@ -1364,17 +1363,20 @@ pub mod pallet { if maybe_check_owner.is_some() { match maybe_item { None => { - let collection_settings = Self::get_collection_settings(&collection)?; + let collection_config = Self::get_collection_config(&collection)?; ensure!( - !collection_settings.contains(CollectionSetting::LockedAttributes), + collection_config + .is_setting_enabled(CollectionSetting::UnlockedAttributes), Error::::LockedCollectionAttributes ) }, Some(item) => { // NOTE: if the item was previously burned, the ItemSettings record might // not exists. In that case, we allow to clear the attribute. - let maybe_is_locked = Self::get_item_settings(&collection, &item) - .map_or(false, |v| v.contains(ItemSetting::LockedAttributes)); + let maybe_is_locked = Self::get_item_config(&collection, &item) + .map_or(false, |c| { + c.has_disabled_setting(ItemSetting::UnlockedAttributes) + }); ensure!(!maybe_is_locked, Error::::LockedItemAttributes); }, }; @@ -1420,19 +1422,19 @@ pub mod pallet { let mut collection_details = Collection::::get(&collection).ok_or(Error::::UnknownCollection)?; - let (action_allowed, _) = - Self::is_item_setting_disabled(&collection, &item, ItemSetting::LockedMetadata)?; + let item_config = Self::get_item_config(&collection, &item)?; ensure!( - maybe_check_owner.is_none() || action_allowed, + maybe_check_owner.is_none() || + item_config.is_setting_enabled(ItemSetting::UnlockedMetadata), Error::::LockedItemMetadata ); - let collection_settings = Self::get_collection_settings(&collection)?; - if let Some(check_owner) = &maybe_check_owner { ensure!(check_owner == &collection_details.owner, Error::::NoPermission); } + let collection_config = Self::get_collection_config(&collection)?; + ItemMetadataOf::::try_mutate_exists(collection, item, |metadata| { if metadata.is_none() { collection_details.item_metadatas.saturating_inc(); @@ -1440,7 +1442,7 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if !collection_settings.contains(CollectionSetting::FreeHolding) && + if collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() @@ -1492,8 +1494,8 @@ pub mod pallet { } // NOTE: if the item was previously burned, the ItemSettings record might not exists - let is_locked = Self::get_item_settings(&collection, &item) - .map_or(false, |v| v.contains(ItemSetting::LockedMetadata)); + let is_locked = Self::get_item_config(&collection, &item) + .map_or(false, |c| c.has_disabled_setting(ItemSetting::UnlockedMetadata)); ensure!(maybe_check_owner.is_none() || !is_locked, Error::::LockedItemMetadata); @@ -1536,12 +1538,10 @@ pub mod pallet { .map(|_| None) .or_else(|origin| ensure_signed(origin).map(Some))?; - let (action_allowed, settings) = Self::is_collection_setting_disabled( - &collection, - CollectionSetting::LockedMetadata, - )?; + let collection_config = Self::get_collection_config(&collection)?; ensure!( - maybe_check_owner.is_none() || action_allowed, + maybe_check_owner.is_none() || + collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata), Error::::LockedCollectionMetadata ); @@ -1555,7 +1555,8 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if maybe_check_owner.is_some() && !settings.contains(CollectionSetting::FreeHolding) + if maybe_check_owner.is_some() && + collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) @@ -1604,12 +1605,10 @@ pub mod pallet { ensure!(check_owner == &details.owner, Error::::NoPermission); } - let (action_allowed, _) = Self::is_collection_setting_disabled( - &collection, - CollectionSetting::LockedMetadata, - )?; + let collection_config = Self::get_collection_config(&collection)?; ensure!( - maybe_check_owner.is_none() || action_allowed, + maybe_check_owner.is_none() || + collection_config.is_setting_enabled(CollectionSetting::UnlockedMetadata), Error::::LockedCollectionMetadata ); diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 362adfc7c5840..d838dd1cb96f1 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -93,11 +93,11 @@ fn events() -> Vec> { } fn default_collection_config() -> CollectionConfig { - CollectionConfig(CollectionSetting::FreeHolding.into()) + CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()) } fn default_item_config() -> ItemConfig { - ItemConfig::empty() + ItemConfig::all_settings_enabled() } #[test] @@ -210,8 +210,8 @@ fn transfer_should_work() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig( - CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + CollectionConfig::disable_settings( + CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit ) )); @@ -236,7 +236,7 @@ fn locking_transfer_should_work() { assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(1), 0, - CollectionConfig(CollectionSetting::NonTransferableItems.into()) + CollectionConfig::disable_settings(CollectionSetting::TransferableItems.into()) )); assert_noop!( Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2), @@ -250,7 +250,7 @@ fn locking_transfer_should_work() { 1, 1, 1, - CollectionConfig::empty(), + CollectionConfig::all_settings_enabled(), )); assert_ok!(Nfts::transfer(RuntimeOrigin::signed(1), 0, 42, 2)); }); @@ -363,7 +363,11 @@ fn set_collection_metadata_should_work() { Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 20]), Error::::UnknownCollection, ); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); // Cannot add metadata to unowned item assert_noop!( Nfts::set_collection_metadata(RuntimeOrigin::signed(2), 0, bvec![0u8; 20]), @@ -396,7 +400,7 @@ fn set_collection_metadata_should_work() { assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(1), 0, - CollectionConfig(CollectionSetting::LockedMetadata.into()) + CollectionConfig::disable_settings(CollectionSetting::UnlockedMetadata.into()) )); assert_noop!( Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0u8; 15]), @@ -432,7 +436,11 @@ fn set_item_metadata_should_work() { Balances::make_free_balance_be(&1, 30); // Cannot add metadata to unknown item - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); // Cannot add metadata to unowned item assert_noop!( @@ -492,7 +500,11 @@ fn set_attribute_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); assert_ok!(Nfts::set_attribute(RuntimeOrigin::signed(1), 0, None, bvec![0], bvec![0])); @@ -538,7 +550,11 @@ fn set_attribute_should_respect_lock() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, default_item_config())); @@ -559,7 +575,7 @@ fn set_attribute_should_respect_lock() { assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(1), 0, - CollectionConfig(CollectionSetting::LockedAttributes.into()) + CollectionConfig::disable_settings(CollectionSetting::UnlockedAttributes.into()) )); let e = Error::::LockedCollectionAttributes; @@ -581,7 +597,11 @@ fn preserve_config_for_frozen_items() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 0, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 1, 1, default_item_config())); @@ -592,7 +612,8 @@ fn preserve_config_for_frozen_items() { // lock the item and ensure the config stays unchanged assert_ok!(Nfts::lock_item_properties(RuntimeOrigin::signed(1), 0, 0, true, true)); - let expect_config = ItemConfig(ItemSetting::LockedAttributes | ItemSetting::LockedMetadata); + let expect_config = + ItemConfig(ItemSetting::UnlockedAttributes | ItemSetting::UnlockedMetadata); let config = ItemConfigOf::::get(0, 0).unwrap(); assert_eq!(config, expect_config); @@ -615,7 +636,11 @@ fn force_collection_status_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 69, 2, default_item_config())); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0; 20])); @@ -631,7 +656,7 @@ fn force_collection_status_should_work() { 1, 1, 1, - CollectionConfig(CollectionSetting::FreeHolding.into()), + CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()), )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2, default_item_config())); @@ -657,7 +682,11 @@ fn force_collection_status_should_work() { fn burn_works() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + 1, + CollectionConfig::all_settings_enabled() + )); assert_ok!(Nfts::set_team(RuntimeOrigin::signed(1), 0, 2, 3, 4)); assert_noop!( @@ -705,8 +734,8 @@ fn approval_lifecycle_works() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig( - CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + CollectionConfig::disable_settings( + CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit ) )); @@ -823,7 +852,7 @@ fn approval_deadline_works() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig(CollectionSetting::FreeHolding.into()) + CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()) )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); @@ -1081,8 +1110,8 @@ fn set_price_should_work() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), user_id, - CollectionConfig( - CollectionSetting::NonTransferableItems | CollectionSetting::FreeHolding + CollectionConfig::disable_settings( + CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit ) )); @@ -1225,7 +1254,7 @@ fn buy_item_should_work() { assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(user_1), collection_id, - CollectionConfig(CollectionSetting::NonTransferableItems.into()) + CollectionConfig::disable_settings(CollectionSetting::TransferableItems.into()) )); let buy_item_call = mock::RuntimeCall::Nfts(crate::Call::::buy_item { @@ -1246,7 +1275,7 @@ fn buy_item_should_work() { user_1, user_1, user_1, - CollectionConfig::empty(), + CollectionConfig::all_settings_enabled(), )); // lock the transfer @@ -1669,24 +1698,23 @@ fn claim_swap_should_work() { fn various_collection_settings() { new_test_ext().execute_with(|| { // when we set only one value it's required to call .into() on it - let config = CollectionConfig(CollectionSetting::NonTransferableItems.into()); + let config = + CollectionConfig::disable_settings(CollectionSetting::TransferableItems.into()); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); let config = CollectionConfigOf::::get(0).unwrap(); - let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); - assert!(!stored_settings.contains(CollectionSetting::LockedMetadata)); + assert!(!config.is_setting_enabled(CollectionSetting::TransferableItems)); + assert!(config.is_setting_enabled(CollectionSetting::UnlockedMetadata)); // no need to call .into() for multiple values - let config = CollectionConfig( - CollectionSetting::LockedMetadata | CollectionSetting::NonTransferableItems, + let config = CollectionConfig::disable_settings( + CollectionSetting::UnlockedMetadata | CollectionSetting::TransferableItems, ); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, config)); let config = CollectionConfigOf::::get(1).unwrap(); - let stored_settings = config.values(); - assert!(stored_settings.contains(CollectionSetting::NonTransferableItems)); - assert!(stored_settings.contains(CollectionSetting::LockedMetadata)); + assert!(!config.is_setting_enabled(CollectionSetting::TransferableItems)); + assert!(!config.is_setting_enabled(CollectionSetting::UnlockedMetadata)); assert_ok!(Nfts::force_create(RuntimeOrigin::root(), 1, default_collection_config())); }); @@ -1698,11 +1726,15 @@ fn collection_locking_should_work() { let user_id = 1; let collection_id = 0; - assert_ok!(Nfts::force_create(RuntimeOrigin::root(), user_id, CollectionConfig::empty())); + assert_ok!(Nfts::force_create( + RuntimeOrigin::root(), + user_id, + CollectionConfig::all_settings_enabled() + )); // validate partial lock - let lock_config = CollectionConfig( - CollectionSetting::NonTransferableItems | CollectionSetting::LockedAttributes, + let lock_config = CollectionConfig::disable_settings( + CollectionSetting::TransferableItems | CollectionSetting::UnlockedAttributes, ); assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(user_id), @@ -1714,15 +1746,15 @@ fn collection_locking_should_work() { assert_eq!(stored_config, lock_config); // validate full lock - let full_lock_config = CollectionConfig( - CollectionSetting::NonTransferableItems | - CollectionSetting::LockedMetadata | - CollectionSetting::LockedAttributes, + let full_lock_config = CollectionConfig::disable_settings( + CollectionSetting::TransferableItems | + CollectionSetting::UnlockedMetadata | + CollectionSetting::UnlockedAttributes, ); assert_ok!(Nfts::lock_collection( RuntimeOrigin::signed(user_id), collection_id, - CollectionConfig(CollectionSetting::LockedMetadata.into()), + CollectionConfig::disable_settings(CollectionSetting::UnlockedMetadata.into()), )); let stored_config = CollectionConfigOf::::get(collection_id).unwrap(); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index 6d47cbfa4a1ef..bbeae8df6f95e 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -182,14 +182,14 @@ pub struct PriceWithDirection { #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum CollectionSetting { - /// Disallow to transfer items in this collection. - NonTransferableItems, - /// Disallow to modify metadata of this collection. - LockedMetadata, - /// Disallow to modify attributes of this collection. - LockedAttributes, - /// When is set then no deposit needed to hold items of this collection. - FreeHolding, + /// Items in this collection are transferable. + TransferableItems, + /// The metadata of this collection can be modified. + UnlockedMetadata, + /// Attributes of this collection can be modified. + UnlockedAttributes, + /// When this isn't set then the deposit is required to hold the items of this collection. + RequiredDeposit, } pub(super) type CollectionSettings = BitFlags; @@ -198,12 +198,27 @@ pub(super) type CollectionSettings = BitFlags; pub struct CollectionConfig(pub CollectionSettings); impl CollectionConfig { - pub fn empty() -> Self { + pub fn all_settings_enabled() -> Self { Self(BitFlags::EMPTY) } - pub fn values(&self) -> CollectionSettings { + pub fn get_disabled_settings(&self) -> CollectionSettings { self.0 } + pub fn is_setting_enabled(&self, setting: CollectionSetting) -> bool { + !self.get_disabled_settings().contains(setting) + } + pub fn has_disabled_setting(&self, setting: CollectionSetting) -> bool { + self.get_disabled_settings().contains(setting) + } + pub fn disable_settings(settings: CollectionSettings) -> Self { + Self(settings) + } + pub fn enable_setting(&mut self, setting: CollectionSetting) { + self.0.remove(setting); + } + pub fn disable_setting(&mut self, setting: CollectionSetting) { + self.0.insert(setting); + } } impl_codec_bitflags!(CollectionConfig, u64, CollectionSetting); @@ -212,12 +227,12 @@ impl_codec_bitflags!(CollectionConfig, u64, CollectionSetting); #[repr(u64)] #[derive(Copy, Clone, RuntimeDebug, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo)] pub enum ItemSetting { - /// Disallow transferring this item. - NonTransferable, - /// Disallow to modify metadata of this item. - LockedMetadata, - /// Disallow to modify attributes of this item. - LockedAttributes, + /// This item is transferable. + Transferable, + /// The metadata of this item can be modified. + UnlockedMetadata, + /// Attributes of this item can be modified. + UnlockedAttributes, } pub(super) type ItemSettings = BitFlags; @@ -226,12 +241,30 @@ pub(super) type ItemSettings = BitFlags; pub struct ItemConfig(pub ItemSettings); impl ItemConfig { - pub fn empty() -> Self { + pub fn all_settings_enabled() -> Self { Self(BitFlags::EMPTY) } - pub fn values(&self) -> ItemSettings { + pub fn get_disabled_settings(&self) -> ItemSettings { self.0 } + pub fn is_setting_enabled(&self, setting: ItemSetting) -> bool { + !self.get_disabled_settings().contains(setting) + } + pub fn has_disabled_setting(&self, setting: ItemSetting) -> bool { + self.get_disabled_settings().contains(setting) + } + pub fn has_disabled_settings(&self) -> bool { + !self.get_disabled_settings().is_empty() + } + pub fn disable_settings(settings: ItemSettings) -> Self { + Self(settings) + } + pub fn enable_setting(&mut self, setting: ItemSetting) { + self.0.remove(setting); + } + pub fn disable_setting(&mut self, setting: ItemSetting) { + self.0.insert(setting); + } } impl_codec_bitflags!(ItemConfig, u64, ItemSetting); From f84563d9f2d2a6a3ba5d62abd9beb2fe6201806c Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 19:15:45 +0300 Subject: [PATCH 58/63] Chore --- frame/nfts/src/features/settings.rs | 2 +- frame/nfts/src/types.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/frame/nfts/src/features/settings.rs b/frame/nfts/src/features/settings.rs index a66f7aca6dd17..2596d360d8dcd 100644 --- a/frame/nfts/src/features/settings.rs +++ b/frame/nfts/src/features/settings.rs @@ -42,6 +42,6 @@ impl, I: 'static> Pallet { pub(crate) fn is_pallet_feature_enabled(feature: PalletFeature) -> bool { let features = T::Features::get(); - return !features.0.contains(feature) + return features.is_enabled(feature) } } diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index bbeae8df6f95e..a1e415161ddf7 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -296,5 +296,8 @@ impl PalletFeatures { pub fn disable(features: BitFlags) -> Self { Self(features) } + pub fn is_enabled(&self, feature: PalletFeature) -> bool { + !self.0.contains(feature) + } } impl_codec_bitflags!(PalletFeatures, u64, PalletFeature); From 05b6d11f1037fb8c99e03188dfb2dbcbac0fdbb8 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Fri, 14 Oct 2022 19:33:54 +0300 Subject: [PATCH 59/63] Make clippy happy --- frame/nfts/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 154e7eba3fcf9..381faa6c9bda8 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -587,7 +587,7 @@ pub mod pallet { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; let admin = T::Lookup::lookup(admin)?; - let mut config = config.clone(); + let mut config = config; // RequiredDeposit could be skipped by calling the force_create() only if !config.is_setting_enabled(CollectionSetting::RequiredDeposit) { config.enable_setting(CollectionSetting::RequiredDeposit); From 08ca688e3c3fb68e0d7f7d0574db3bc5d289bf54 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Sat, 15 Oct 2022 12:35:55 +0300 Subject: [PATCH 60/63] Chore --- frame/nfts/src/impl_nonfungibles.rs | 7 ++++++- frame/nfts/src/lib.rs | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 3d9ac339dd653..785d892cf6335 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -101,11 +101,16 @@ impl, I: 'static> Create<::AccountId, Collection admin: &T::AccountId, disabled_settings: &CollectionSettings, ) -> DispatchResult { + let mut disabled_settings = *disabled_settings; + // RequiredDeposit can be disabled by calling the force_create() only + if disabled_settings.contains(CollectionSetting::RequiredDeposit) { + disabled_settings.remove(CollectionSetting::RequiredDeposit); + } Self::do_create_collection( *collection, who.clone(), admin.clone(), - CollectionConfig::disable_settings(*disabled_settings), + CollectionConfig::disable_settings(disabled_settings), T::CollectionDeposit::get(), Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, ) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 381faa6c9bda8..a1e7a75ef6936 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -588,8 +588,8 @@ pub mod pallet { let admin = T::Lookup::lookup(admin)?; let mut config = config; - // RequiredDeposit could be skipped by calling the force_create() only - if !config.is_setting_enabled(CollectionSetting::RequiredDeposit) { + // RequiredDeposit could be disabled by calling the force_create() only + if config.has_disabled_setting(CollectionSetting::RequiredDeposit) { config.enable_setting(CollectionSetting::RequiredDeposit); } From 8f54489641de9f6b762111ad96155edbbb6c4dbb Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 17 Oct 2022 16:44:54 +0300 Subject: [PATCH 61/63] Update comment --- frame/nfts/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index a1e7a75ef6936..1665f9071cea4 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -506,7 +506,7 @@ pub mod pallet { WrongDelegate, /// No approval exists that would allow the transfer. Unapproved, - /// The named owner has not signed ownership of the collection is acceptable. + /// The named owner has not signed ownership acceptance of the collection. Unaccepted, /// The item is locked (non-transferable). ItemLocked, From 8bb43e64324982623c919860533f31c013bee14a Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Mon, 17 Oct 2022 17:24:13 +0300 Subject: [PATCH 62/63] RequiredDeposit => DepositRequired --- frame/nfts/src/benchmarking.rs | 4 ++-- frame/nfts/src/functions.rs | 2 +- frame/nfts/src/impl_nonfungibles.rs | 6 +++--- frame/nfts/src/lib.rs | 14 +++++++------- frame/nfts/src/tests.rs | 12 ++++++------ frame/nfts/src/types.rs | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/frame/nfts/src/benchmarking.rs b/frame/nfts/src/benchmarking.rs index 11950395b7ad0..c65430fd35108 100644 --- a/frame/nfts/src/benchmarking.rs +++ b/frame/nfts/src/benchmarking.rs @@ -204,7 +204,7 @@ benchmarks_instance_pallet! { caller_lookup.clone(), caller_lookup.clone(), caller_lookup, - CollectionConfig(CollectionSetting::RequiredDeposit.into()), + CollectionConfig(CollectionSetting::DepositRequired.into()), )?; }: _(SystemOrigin::Signed(caller.clone()), collection, items.clone()) verify { @@ -280,7 +280,7 @@ benchmarks_instance_pallet! { issuer: caller_lookup.clone(), admin: caller_lookup.clone(), freezer: caller_lookup, - config: CollectionConfig(CollectionSetting::RequiredDeposit.into()), + config: CollectionConfig(CollectionSetting::DepositRequired.into()), }; }: { call.dispatch_bypass_filter(origin)? } verify { diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index e52fc1d9a27e8..2985331e39028 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -188,7 +188,7 @@ impl, I: 'static> Pallet { let collection_config = Self::get_collection_config(&collection)?; let deposit = match collection_config - .is_setting_enabled(CollectionSetting::RequiredDeposit) + .is_setting_enabled(CollectionSetting::DepositRequired) { true => T::ItemDeposit::get(), false => Zero::zero(), diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index 785d892cf6335..fdd194f3e6f60 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -102,9 +102,9 @@ impl, I: 'static> Create<::AccountId, Collection disabled_settings: &CollectionSettings, ) -> DispatchResult { let mut disabled_settings = *disabled_settings; - // RequiredDeposit can be disabled by calling the force_create() only - if disabled_settings.contains(CollectionSetting::RequiredDeposit) { - disabled_settings.remove(CollectionSetting::RequiredDeposit); + // DepositRequired can be disabled by calling the force_create() only + if disabled_settings.contains(CollectionSetting::DepositRequired) { + disabled_settings.remove(CollectionSetting::DepositRequired); } Self::do_create_collection( *collection, diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index 1665f9071cea4..a9519bdfe1980 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -588,9 +588,9 @@ pub mod pallet { let admin = T::Lookup::lookup(admin)?; let mut config = config; - // RequiredDeposit could be disabled by calling the force_create() only - if config.has_disabled_setting(CollectionSetting::RequiredDeposit) { - config.enable_setting(CollectionSetting::RequiredDeposit); + // DepositRequired could be disabled by calling the force_create() only + if config.has_disabled_setting(CollectionSetting::DepositRequired) { + config.enable_setting(CollectionSetting::DepositRequired); } Self::do_create_collection( @@ -810,7 +810,7 @@ pub mod pallet { ensure!(collection_details.owner == origin, Error::::NoPermission); let config = Self::get_collection_config(&collection)?; - let deposit = match config.is_setting_enabled(CollectionSetting::RequiredDeposit) { + let deposit = match config.is_setting_enabled(CollectionSetting::DepositRequired) { true => T::ItemDeposit::get(), false => Zero::zero(), }; @@ -1309,7 +1309,7 @@ pub mod pallet { let old_deposit = attribute.map_or(Zero::zero(), |m| m.1); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) && + if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() @@ -1442,7 +1442,7 @@ pub mod pallet { let old_deposit = metadata.take().map_or(Zero::zero(), |m| m.deposit); collection_details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); - if collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) && + if collection_config.is_setting_enabled(CollectionSetting::DepositRequired) && maybe_check_owner.is_some() { deposit = T::DepositPerByte::get() @@ -1556,7 +1556,7 @@ pub mod pallet { details.total_deposit.saturating_reduce(old_deposit); let mut deposit = Zero::zero(); if maybe_check_owner.is_some() && - collection_config.is_setting_enabled(CollectionSetting::RequiredDeposit) + collection_config.is_setting_enabled(CollectionSetting::DepositRequired) { deposit = T::DepositPerByte::get() .saturating_mul(((data.len()) as u32).into()) diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index d838dd1cb96f1..59da2b5a18452 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -93,7 +93,7 @@ fn events() -> Vec> { } fn default_collection_config() -> CollectionConfig { - CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()) + CollectionConfig::disable_settings(CollectionSetting::DepositRequired.into()) } fn default_item_config() -> ItemConfig { @@ -211,7 +211,7 @@ fn transfer_should_work() { RuntimeOrigin::root(), 1, CollectionConfig::disable_settings( - CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit + CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); @@ -656,7 +656,7 @@ fn force_collection_status_should_work() { 1, 1, 1, - CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()), + CollectionConfig::disable_settings(CollectionSetting::DepositRequired.into()), )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 142, 1, default_item_config())); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 169, 2, default_item_config())); @@ -735,7 +735,7 @@ fn approval_lifecycle_works() { RuntimeOrigin::root(), 1, CollectionConfig::disable_settings( - CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit + CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); @@ -852,7 +852,7 @@ fn approval_deadline_works() { assert_ok!(Nfts::force_create( RuntimeOrigin::root(), 1, - CollectionConfig::disable_settings(CollectionSetting::RequiredDeposit.into()) + CollectionConfig::disable_settings(CollectionSetting::DepositRequired.into()) )); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 2, default_item_config())); @@ -1111,7 +1111,7 @@ fn set_price_should_work() { RuntimeOrigin::root(), user_id, CollectionConfig::disable_settings( - CollectionSetting::TransferableItems | CollectionSetting::RequiredDeposit + CollectionSetting::TransferableItems | CollectionSetting::DepositRequired ) )); diff --git a/frame/nfts/src/types.rs b/frame/nfts/src/types.rs index a1e415161ddf7..6ed57e4da25e5 100644 --- a/frame/nfts/src/types.rs +++ b/frame/nfts/src/types.rs @@ -189,7 +189,7 @@ pub enum CollectionSetting { /// Attributes of this collection can be modified. UnlockedAttributes, /// When this isn't set then the deposit is required to hold the items of this collection. - RequiredDeposit, + DepositRequired, } pub(super) type CollectionSettings = BitFlags; From 41a1c83ac2d0cd11d9451faa483a69d1406193a0 Mon Sep 17 00:00:00 2001 From: Jegor Sidorenko Date: Tue, 18 Oct 2022 10:33:42 +0300 Subject: [PATCH 63/63] Address comments --- frame/nfts/src/functions.rs | 3 +-- frame/nfts/src/impl_nonfungibles.rs | 15 ++++++++------- frame/nfts/src/lib.rs | 14 +++++++------- frame/nfts/src/tests.rs | 18 +++++++++++++++--- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/frame/nfts/src/functions.rs b/frame/nfts/src/functions.rs index 2985331e39028..275e3668d7a20 100644 --- a/frame/nfts/src/functions.rs +++ b/frame/nfts/src/functions.rs @@ -148,8 +148,7 @@ impl, I: 'static> Pallet { T::Currency::unreserve(&collection_details.owner, collection_details.total_deposit); CollectionMaxSupply::::remove(&collection); CollectionConfigOf::::remove(&collection); - #[allow(deprecated)] - ItemConfigOf::::remove_prefix(&collection, None); + let _ = ItemConfigOf::::clear_prefix(&collection, witness.items, None); Self::deposit_event(Event::Destroyed { collection }); diff --git a/frame/nfts/src/impl_nonfungibles.rs b/frame/nfts/src/impl_nonfungibles.rs index fdd194f3e6f60..8a7c79fc0c14f 100644 --- a/frame/nfts/src/impl_nonfungibles.rs +++ b/frame/nfts/src/impl_nonfungibles.rs @@ -19,6 +19,7 @@ use super::*; use frame_support::{ + ensure, traits::{tokens::nonfungibles_v2::*, Get}, BoundedSlice, }; @@ -91,7 +92,7 @@ impl, I: 'static> Inspect<::AccountId> for Palle } } -impl, I: 'static> Create<::AccountId, CollectionSettings> +impl, I: 'static> Create<::AccountId, CollectionConfig> for Pallet { /// Create a `collection` of nonfungible items to be owned by `who` and managed by `admin`. @@ -99,18 +100,18 @@ impl, I: 'static> Create<::AccountId, Collection collection: &Self::CollectionId, who: &T::AccountId, admin: &T::AccountId, - disabled_settings: &CollectionSettings, + config: &CollectionConfig, ) -> DispatchResult { - let mut disabled_settings = *disabled_settings; // DepositRequired can be disabled by calling the force_create() only - if disabled_settings.contains(CollectionSetting::DepositRequired) { - disabled_settings.remove(CollectionSetting::DepositRequired); - } + ensure!( + !config.has_disabled_setting(CollectionSetting::DepositRequired), + Error::::WrongSetting + ); Self::do_create_collection( *collection, who.clone(), admin.clone(), - CollectionConfig::disable_settings(disabled_settings), + *config, T::CollectionDeposit::get(), Event::Created { collection: *collection, creator: who.clone(), owner: admin.clone() }, ) diff --git a/frame/nfts/src/lib.rs b/frame/nfts/src/lib.rs index a9519bdfe1980..bfba0c1ea3330 100644 --- a/frame/nfts/src/lib.rs +++ b/frame/nfts/src/lib.rs @@ -498,8 +498,6 @@ pub mod pallet { CollectionIdInUse, /// Items within that collection are non-transferable. ItemsNotTransferable, - /// The item or collection is frozen. - Frozen, /// The provided account is not a delegate. NotDelegate, /// The delegate turned out to be different to what was expected. @@ -540,6 +538,8 @@ pub mod pallet { WrongDuration, /// The method is disabled by system settings. MethodDisabled, + /// The provided is setting can't be set. + WrongSetting, /// Item's config already exists and should be equal to the provided one. InconsistentItemConfig, /// Config for a collection or an item can't be found. @@ -587,11 +587,11 @@ pub mod pallet { let owner = T::CreateOrigin::ensure_origin(origin, &collection)?; let admin = T::Lookup::lookup(admin)?; - let mut config = config; - // DepositRequired could be disabled by calling the force_create() only - if config.has_disabled_setting(CollectionSetting::DepositRequired) { - config.enable_setting(CollectionSetting::DepositRequired); - } + // DepositRequired can be disabled by calling the force_create() only + ensure!( + !config.has_disabled_setting(CollectionSetting::DepositRequired), + Error::::WrongSetting + ); Self::do_create_collection( collection, diff --git a/frame/nfts/src/tests.rs b/frame/nfts/src/tests.rs index 59da2b5a18452..1b60fd6431b19 100644 --- a/frame/nfts/src/tests.rs +++ b/frame/nfts/src/tests.rs @@ -126,7 +126,11 @@ fn basic_minting_should_work() { fn lifecycle_should_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(1), + 1, + CollectionConfig::all_settings_enabled() + )); assert_eq!(Balances::reserved_balance(&1), 2); assert_eq!(collections(), vec![(1, 0)]); assert_ok!(Nfts::set_collection_metadata(RuntimeOrigin::signed(1), 0, bvec![0, 0])); @@ -170,7 +174,11 @@ fn lifecycle_should_work() { fn destroy_with_bad_witness_should_not_work() { new_test_ext().execute_with(|| { Balances::make_free_balance_be(&1, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(1), + 1, + CollectionConfig::all_settings_enabled() + )); let w = Collection::::get(0).unwrap().destroy_witness(); assert_ok!(Nfts::mint(RuntimeOrigin::signed(1), 0, 42, 1, default_item_config())); @@ -299,7 +307,11 @@ fn transfer_owner_should_work() { Balances::make_free_balance_be(&1, 100); Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 100); - assert_ok!(Nfts::create(RuntimeOrigin::signed(1), 1, default_collection_config())); + assert_ok!(Nfts::create( + RuntimeOrigin::signed(1), + 1, + CollectionConfig::all_settings_enabled() + )); assert_eq!(collections(), vec![(1, 0)]); assert_noop!( Nfts::transfer_ownership(RuntimeOrigin::signed(1), 0, 2),