Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata for Base and Parts #273

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 32 additions & 20 deletions pallets/rmrk-core/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,40 +59,52 @@ impl<T: Config> Property<KeyLimitOf<T>, ValueLimitOf<T>, T::AccountId, T::Collec
{
fn property_set(
sender: T::AccountId,
collection_id: T::CollectionId,
maybe_nft_id: Option<T::ItemId>,
entity: EntityOf<T>,
key: KeyLimitOf<T>,
value: ValueLimitOf<T>,
) -> DispatchResult {
let collection =
Collections::<T>::get(&collection_id).ok_or(Error::<T>::CollectionUnknown)?;
ensure!(collection.issuer == sender, Error::<T>::NoPermission);
if let Some(nft_id) = &maybe_nft_id {
// Check NFT lock status
ensure!(
!Pallet::<T>::is_locked(collection_id, *nft_id),
pallet_uniques::Error::<T>::Locked
);
let budget = budget::Value::new(T::NestingBudget::get());
let (root_owner, _) = Pallet::<T>::lookup_root_owner(collection_id, *nft_id, &budget)?;
ensure!(root_owner == collection.issuer, Error::<T>::NoPermission);
match entity {
Entity::Collection(_collection_id) => todo!(),
Entity::Nft(collection_id, nft_id) => {
let collection =
Collections::<T>::get(&collection_id).ok_or(Error::<T>::CollectionUnknown)?;
ensure!(collection.issuer == sender, Error::<T>::NoPermission);
// Check NFT lock status
ensure!(
!Pallet::<T>::is_locked(collection_id, nft_id),
pallet_uniques::Error::<T>::Locked
);
let budget = budget::Value::new(T::NestingBudget::get());
let (root_owner, _) =
Pallet::<T>::lookup_root_owner(collection_id, nft_id, &budget)?;
ensure!(root_owner == collection.issuer, Error::<T>::NoPermission);

Properties::<T>::insert((&collection_id, Some(nft_id), &key), &value);
},
Entity::Base(_base_id) => todo!(),
Entity::Part(_part_id) => todo!(),
}
Properties::<T>::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<T::ItemId>,
entity: EntityOf<T>,
key: KeyLimitOf<T>,
value: ValueLimitOf<T>,
) -> sp_runtime::DispatchResult {
// Ensure collection exists
Collections::<T>::get(&collection_id).ok_or(Error::<T>::CollectionUnknown)?;
Properties::<T>::insert((&collection_id, maybe_nft_id, &key), &value);
match entity {
Entity::Collection(_collection_id) => todo!(),
Entity::Nft(collection_id, nft_id) => {
Collections::<T>::get(&collection_id).ok_or(Error::<T>::CollectionUnknown)?;
Properties::<T>::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(())
}

Expand Down
28 changes: 20 additions & 8 deletions pallets/rmrk-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -86,6 +86,13 @@ pub type BoundedResourceInfoTypeOf<T> = BoundedVec<

pub type PropertyInfoOf<T> = PropertyInfo<KeyLimitOf<T>, ValueLimitOf<T>>;

pub type EntityOf<T> = Entity<
<T as pallet_uniques::Config>::CollectionId,
<T as pallet_uniques::Config>::ItemId,
BaseId,
PartId,
>;

pub mod types;

// Re-export pallet items so that they can be accessed from the crate namespace.
Expand Down Expand Up @@ -266,6 +273,12 @@ pub mod pallet {
OptionQuery,
>;

#[pallet::storage]
#[pallet::getter(fn base_properties)]
/// Arbitrary properties / metadata of a base.
pub type BaseProperties<T: Config> =
StorageMap<_, Twox64Concat, BaseId, ValueLimitOf<T>, OptionQuery>;

#[pallet::storage]
#[pallet::getter(fn lock)]
/// Lock for NFTs
Expand Down Expand Up @@ -335,8 +348,7 @@ pub mod pallet {
collection_id: T::CollectionId,
},
PropertySet {
collection_id: T::CollectionId,
maybe_nft_id: Option<T::ItemId>,
entity: EntityOf<T>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes will cause chain indexing problems for PhalaWorld. @h4x3rotab do you know a best practice to enable this Entity feature so we can ensure that this change doesn't break our indexers for PW and Stakepool?

key: KeyLimitOf<T>,
value: ValueLimitOf<T>,
},
Expand Down Expand Up @@ -713,18 +725,18 @@ pub mod pallet {
#[transactional]
pub fn set_property(
origin: OriginFor<T>,
collection_id: T::CollectionId,
maybe_nft_id: Option<T::ItemId>,
entity: EntityOf<T>,
key: KeyLimitOf<T>,
value: ValueLimitOf<T>,
) -> 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(<T as pallet::Config>::WeightInfo::lock_collection())]
Expand Down
28 changes: 15 additions & 13 deletions pallets/rmrk-core/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Test>::CollectionUnknown
);
// Create a basic collection
Expand All @@ -1826,23 +1828,21 @@ 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(),
}));
// Property value now exists
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::<Test>::NoPermission
);
});
Expand All @@ -1855,21 +1855,22 @@ 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::<Test>::CollectionUnknown
);
// 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(),
}));
Expand All @@ -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(),
}));
Expand Down
19 changes: 19 additions & 0 deletions traits/src/entity.rs
Original file line number Diff line number Diff line change
@@ -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<CollectionId, ItemId, BaseId, PartId> {
/// 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),
}
2 changes: 2 additions & 0 deletions traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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};
Expand Down
10 changes: 6 additions & 4 deletions traits/src/property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -35,16 +39,14 @@ pub struct PropertyInfo<BoundedKey, BoundedValue> {
pub trait Property<KeyLimit, ValueLimit, AccountId, CollectionId, NftId> {
fn property_set(
sender: AccountId,
collection_id: CollectionId,
maybe_nft_id: Option<NftId>,
enity: Entity<CollectionId, NftId, BaseId, PartId>,
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<NftId>,
entity: Entity<CollectionId, NftId, BaseId, PartId>,
key: KeyLimit,
value: ValueLimit,
) -> DispatchResult;
Expand Down