diff --git a/pallets/rmrk-core/src/functions.rs b/pallets/rmrk-core/src/functions.rs index f688f439..2eafa5a0 100644 --- a/pallets/rmrk-core/src/functions.rs +++ b/pallets/rmrk-core/src/functions.rs @@ -59,40 +59,52 @@ impl Property, ValueLimitOf, T::AccountId, T::Collec { fn property_set( sender: T::AccountId, - collection_id: T::CollectionId, - maybe_nft_id: Option, + entity: EntityOf, key: KeyLimitOf, value: ValueLimitOf, ) -> DispatchResult { - let collection = - Collections::::get(&collection_id).ok_or(Error::::CollectionUnknown)?; - ensure!(collection.issuer == sender, Error::::NoPermission); - if let Some(nft_id) = &maybe_nft_id { - // Check NFT lock status - ensure!( - !Pallet::::is_locked(collection_id, *nft_id), - pallet_uniques::Error::::Locked - ); - let budget = budget::Value::new(T::NestingBudget::get()); - let (root_owner, _) = Pallet::::lookup_root_owner(collection_id, *nft_id, &budget)?; - ensure!(root_owner == collection.issuer, Error::::NoPermission); + match entity { + Entity::Collection(_collection_id) => todo!(), + Entity::Nft(collection_id, nft_id) => { + let collection = + Collections::::get(&collection_id).ok_or(Error::::CollectionUnknown)?; + ensure!(collection.issuer == sender, Error::::NoPermission); + // Check NFT lock status + ensure!( + !Pallet::::is_locked(collection_id, nft_id), + pallet_uniques::Error::::Locked + ); + let budget = budget::Value::new(T::NestingBudget::get()); + let (root_owner, _) = + Pallet::::lookup_root_owner(collection_id, nft_id, &budget)?; + ensure!(root_owner == collection.issuer, Error::::NoPermission); + + Properties::::insert((&collection_id, Some(nft_id), &key), &value); + }, + Entity::Base(_base_id) => todo!(), + Entity::Part(_part_id) => todo!(), } - Properties::::insert((&collection_id, maybe_nft_id, &key), &value); Ok(()) } // Internal function to set a property for downstream `Origin::root()` calls. fn do_set_property( - collection_id: T::CollectionId, - maybe_nft_id: Option, + entity: EntityOf, key: KeyLimitOf, value: ValueLimitOf, ) -> sp_runtime::DispatchResult { // Ensure collection exists - Collections::::get(&collection_id).ok_or(Error::::CollectionUnknown)?; - Properties::::insert((&collection_id, maybe_nft_id, &key), &value); + match entity { + Entity::Collection(_collection_id) => todo!(), + Entity::Nft(collection_id, nft_id) => { + Collections::::get(&collection_id).ok_or(Error::::CollectionUnknown)?; + Properties::::insert((&collection_id, Some(nft_id), &key), &value); + }, + Entity::Base(_base_id) => todo!(), + Entity::Part(_part_id) => todo!(), + } - Self::deposit_event(Event::PropertySet { collection_id, maybe_nft_id, key, value }); + Self::deposit_event(Event::PropertySet { entity, key, value }); Ok(()) } diff --git a/pallets/rmrk-core/src/lib.rs b/pallets/rmrk-core/src/lib.rs index 079f8b91..72e12ea8 100644 --- a/pallets/rmrk-core/src/lib.rs +++ b/pallets/rmrk-core/src/lib.rs @@ -22,8 +22,8 @@ use rmrk_traits::{ misc::TransferHooks, primitives::{BaseId, PartId, ResourceId, SlotId}, AccountIdOrCollectionNftTuple, BasicResource, Collection, CollectionInfo, ComposableResource, - Nft, NftChild, NftInfo, PhantomType, Priority, Property, PropertyInfo, Resource, ResourceInfo, - ResourceInfoMin, ResourceTypes, RoyaltyInfo, SlotResource, + Entity, Nft, NftChild, NftInfo, PhantomType, Priority, Property, PropertyInfo, Resource, + ResourceInfo, ResourceInfoMin, ResourceTypes, RoyaltyInfo, SlotResource, }; use sp_std::result::Result; @@ -86,6 +86,13 @@ pub type BoundedResourceInfoTypeOf = BoundedVec< pub type PropertyInfoOf = PropertyInfo, ValueLimitOf>; +pub type EntityOf = Entity< + ::CollectionId, + ::ItemId, + BaseId, + PartId, +>; + pub mod types; // Re-export pallet items so that they can be accessed from the crate namespace. @@ -266,6 +273,12 @@ pub mod pallet { OptionQuery, >; + #[pallet::storage] + #[pallet::getter(fn base_properties)] + /// Arbitrary properties / metadata of a base. + pub type BaseProperties = + StorageMap<_, Twox64Concat, BaseId, ValueLimitOf, OptionQuery>; + #[pallet::storage] #[pallet::getter(fn lock)] /// Lock for NFTs @@ -335,8 +348,7 @@ pub mod pallet { collection_id: T::CollectionId, }, PropertySet { - collection_id: T::CollectionId, - maybe_nft_id: Option, + entity: EntityOf, key: KeyLimitOf, value: ValueLimitOf, }, @@ -713,18 +725,18 @@ pub mod pallet { #[transactional] pub fn set_property( origin: OriginFor, - collection_id: T::CollectionId, - maybe_nft_id: Option, + entity: EntityOf, key: KeyLimitOf, value: ValueLimitOf, ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::property_set(sender, collection_id, maybe_nft_id, key.clone(), value.clone())?; + Self::property_set(sender, entity.clone(), key.clone(), value.clone())?; - Self::deposit_event(Event::PropertySet { collection_id, maybe_nft_id, key, value }); + Self::deposit_event(Event::PropertySet { entity, key, value }); Ok(()) } + /// lock collection #[pallet::call_index(10)] #[pallet::weight(::WeightInfo::lock_collection())] diff --git a/pallets/rmrk-core/src/tests.rs b/pallets/rmrk-core/src/tests.rs index 629e4f8a..4f3ff8fa 100644 --- a/pallets/rmrk-core/src/tests.rs +++ b/pallets/rmrk-core/src/tests.rs @@ -1814,9 +1814,11 @@ fn set_property_works() { let key = stbk("test-key"); // Define property value let value = stb("test-value"); + // Define the entity. + let nft = Entity::Nft(0, 0); // set_property fails without a collection (CollectionUnknown) assert_noop!( - RMRKCore::set_property(Origin::signed(ALICE), 0, Some(0), key.clone(), value.clone()), + RMRKCore::set_property(Origin::signed(ALICE), nft.clone(), key.clone(), value.clone()), Error::::CollectionUnknown ); // Create a basic collection @@ -1826,15 +1828,13 @@ fn set_property_works() { // ALICE sets property on NFT assert_ok!(RMRKCore::set_property( Origin::signed(ALICE), - 0, - Some(0), + nft.clone(), key.clone(), value.clone() )); // Successful property setting should trigger a PropertySet event System::assert_last_event(MockEvent::RmrkCore(crate::Event::PropertySet { - collection_id: 0, - maybe_nft_id: Some(0), + entity: nft.clone(), key: key.clone(), value: value.clone(), })); @@ -1842,7 +1842,7 @@ fn set_property_works() { assert_eq!(RMRKCore::properties((0, Some(0), key.clone())).unwrap(), value.clone()); // BOB does not own NFT so attempt to set property should fail assert_noop!( - RMRKCore::set_property(Origin::signed(BOB), 0, Some(0), key.clone(), value.clone()), + RMRKCore::set_property(Origin::signed(BOB), nft, key.clone(), value.clone()), Error::::NoPermission ); }); @@ -1855,9 +1855,11 @@ fn set_property_with_internal_works() { let key = stbk("test-key"); // Define property value let value = stb("test-value"); + // Define the entity. + let nft = Entity::Nft(0, 0); // set_property fails without a collection (CollectionUnknown) assert_noop!( - RMRKCore::do_set_property(0, Some(0), key.clone(), value.clone()), + RMRKCore::do_set_property(nft.clone(), key.clone(), value.clone()), Error::::CollectionUnknown ); // Create a basic collection @@ -1865,11 +1867,10 @@ fn set_property_with_internal_works() { // Mint NFT assert_ok!(basic_mint(0)); // Root sets property on NFT - assert_ok!(RMRKCore::do_set_property(0, Some(0), key.clone(), value.clone())); + assert_ok!(RMRKCore::do_set_property(nft.clone(), key.clone(), value.clone())); // Successful property setting should trigger a `PropertySet` event System::assert_last_event(MockEvent::RmrkCore(crate::Event::PropertySet { - collection_id: 0, - maybe_nft_id: Some(0), + entity: nft, key: key.clone(), value: value.clone(), })); @@ -1885,16 +1886,17 @@ fn remove_property_with_internal_works() { let key = stbk("test-key"); // Define property value let value = stb("test-value"); + // Define the entity. + let nft = Entity::Nft(0, 0); // Create a basic collection assert_ok!(basic_collection()); // Mint NFT assert_ok!(basic_mint(0)); // Root sets property on NFT - assert_ok!(RMRKCore::do_set_property(0, Some(0), key.clone(), value.clone())); + assert_ok!(RMRKCore::do_set_property(nft.clone(), key.clone(), value.clone())); // Successful property setting should trigger a PropertySet event System::assert_last_event(MockEvent::RmrkCore(crate::Event::PropertySet { - collection_id: 0, - maybe_nft_id: Some(0), + entity: nft, key: key.clone(), value: value.clone(), })); diff --git a/traits/src/entity.rs b/traits/src/entity.rs new file mode 100644 index 00000000..71e0546f --- /dev/null +++ b/traits/src/entity.rs @@ -0,0 +1,19 @@ +// Copyright (C) 2021-2022 RMRK +// This file is part of rmrk-substrate. +// License: Apache 2.0 modified by RMRK, see LICENSE.md + +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::*; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen, Clone, PartialEq)] +pub enum Entity { + /// The entity is a collection + Collection(CollectionId), + /// The entity is a collection nft + Nft(CollectionId, ItemId), + /// The entity is a base + Base(BaseId), + /// The entity is a Part + Part(PartId), +} diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 2aba091f..118300bb 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -7,6 +7,7 @@ pub mod base; pub mod budget; pub mod collection; +pub mod entity; pub mod misc; pub mod nft; pub mod part; @@ -19,6 +20,7 @@ pub mod theme; pub use base::{Base, BaseInfo}; pub use collection::{Collection, CollectionInfo}; +pub use entity::Entity; pub use misc::TransferHooks; pub use nft::{AccountIdOrCollectionNftTuple, Nft, NftChild, NftInfo, RoyaltyInfo}; pub use part::{EquippableList, FixedPart, PartType, SlotPart}; diff --git a/traits/src/property.rs b/traits/src/property.rs index f519aaa8..2cee21db 100644 --- a/traits/src/property.rs +++ b/traits/src/property.rs @@ -2,6 +2,10 @@ // This file is part of rmrk-substrate. // License: Apache 2.0 modified by RMRK, see LICENSE.md +use crate::{ + primitives::{BaseId, PartId}, + Entity, +}; use codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_runtime::DispatchResult; @@ -35,16 +39,14 @@ pub struct PropertyInfo { pub trait Property { fn property_set( sender: AccountId, - collection_id: CollectionId, - maybe_nft_id: Option, + enity: Entity, key: KeyLimit, value: ValueLimit, ) -> DispatchResult; /// Internal function to set a property that can be called from `Origin::root()` downstream. fn do_set_property( - collection_id: CollectionId, - maybe_nft_id: Option, + entity: Entity, key: KeyLimit, value: ValueLimit, ) -> DispatchResult;