From 5c5d937f9f7ff1dddbb1f4cd93b50e4311a64ed1 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 25 May 2022 23:31:03 +0000 Subject: [PATCH] refactor iterate rmrk props, add rmrk proxy set_propertty --- --- a/pallets/proxy-rmrk-core/src/lib.rs +++ b/pallets/proxy-rmrk-core/src/lib.rs @@ -24,6 +24,7 @@ use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations}; use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData}; use pallet_evm::account::CrossAccountId; +use core::convert::AsRef; pub use pallet::*; @@ -85,6 +86,12 @@ owner: T::AccountId, nft_id: RmrkNftId, }, + PropertySet { + collection_id: RmrkCollectionId, + maybe_nft_id: Option, + key: RmrkKeyString, + value: RmrkValueString, + }, } #[pallet::error] @@ -291,7 +298,7 @@ collection_id: RmrkCollectionId, nft_id: RmrkNftId, ) -> DispatchResult { - let sender = ensure_signed(origin.clone())?; + let sender = ensure_signed(origin)?; let cross_sender = T::CrossAccountId::from_sub(sender.clone()); Self::destroy_nft( @@ -305,6 +312,62 @@ Ok(()) } + + #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))] + #[transactional] + pub fn set_property( + origin: OriginFor, + #[pallet::compact] rmrk_collection_id: RmrkCollectionId, + maybe_nft_id: Option, + key: RmrkKeyString, + value: RmrkValueString, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let sender = T::CrossAccountId::from_sub(sender); + + let collection_id: CollectionId = rmrk_collection_id.into(); + + match maybe_nft_id { + Some(nft_id) => { + let token_id: TokenId = nft_id.into(); + + Self::ensure_nft_owner(collection_id, token_id, &sender)?; + Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?; + + >::set_scoped_token_property( + collection_id, + token_id, + PropertyScope::Rmrk, + Self::rmrk_property(UserProperty(key.as_slice()), &value)? + )?; + }, + None => { + let collection = Self::get_typed_nft_collection( + collection_id, + misc::CollectionType::Regular + )?; + + Self::check_collection_owner(&collection, &sender)?; + + >::set_scoped_collection_property( + collection_id, + PropertyScope::Rmrk, + Self::rmrk_property(UserProperty(key.as_slice()), &value)? + )?; + } + } + + Self::deposit_event( + Event::PropertySet { + collection_id: rmrk_collection_id, + maybe_nft_id, + key, + value + } + ); + + Ok(()) + } } } @@ -477,65 +540,91 @@ pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult { let actual_type = Self::get_nft_type(collection_id, token_id)?; - ensure!(actual_type == nft_type, >::NoPermission); + ensure!(actual_type == nft_type, >::NoPermission); Ok(()) } - pub fn filter_theme_properties( + pub fn ensure_nft_owner( collection_id: CollectionId, token_id: TokenId, - filter_keys: Option> - ) -> Result, DispatchError> { + possible_owner: &T::CrossAccountId + ) -> DispatchResult { + let token_data = >::get((collection_id, token_id)) + .ok_or(>::NoAvailableNftId)?; + + ensure!(token_data.owner == *possible_owner, >::NoPermission); + + Ok(()) + } + + pub fn filter_user_properties( + collection_id: CollectionId, + token_id: Option, + filter_keys: Option>, + mapper: Mapper, + ) -> Result, DispatchError> + where + Key: TryFrom + AsRef<[u8]>, + Value: Decode + Default, + Mapper: Fn(Key, Value) -> R + { filter_keys.map(|keys| { let properties = keys.into_iter() .filter_map(|key| { - let key: RmrkString = key.try_into().ok()?; + let key: Key = key.try_into().ok()?; - let value = Self::get_nft_property( - collection_id, - token_id, - ThemeProperty(&key) - ).ok()?.decode_or_default(); - - let property = RmrkThemeProperty { - key, - value - }; + let value = match token_id { + Some(token_id) => Self::get_nft_property( + collection_id, + token_id, + UserProperty(key.as_ref()) + ), + None => Self::get_collection_property( + collection_id, + UserProperty(key.as_ref()) + ) + }.ok()?.decode_or_default(); - Some(property) + Some(mapper(key, value)) }) .collect(); Ok(properties) }).unwrap_or_else(|| { - let properties = Self::iterate_theme_properties(collection_id, token_id)? + let properties = Self::iterate_user_properties(collection_id, token_id, mapper)? .collect(); Ok(properties) }) } - pub fn iterate_theme_properties( + pub fn iterate_user_properties( collection_id: CollectionId, - token_id: TokenId - ) -> Result, DispatchError> { - let key_prefix = Self::rmrk_property_key(ThemeProperty(&RmrkString::default()))?; + token_id: Option, + mapper: Mapper, + ) -> Result, DispatchError> + where + Key: TryFrom + AsRef<[u8]>, + Value: Decode + Default, + Mapper: Fn(Key, Value) -> R + { + let key_prefix = Self::rmrk_property_key(UserProperty(b""))?; - let properties = >::token_properties((collection_id, token_id)) + let properties = match token_id { + Some(token_id) => >::token_properties((collection_id, token_id)), + None => >::collection_properties(collection_id) + }; + + let properties = properties .into_iter() .filter_map(move |(key, value)| { let key = key.as_slice().strip_prefix(key_prefix.as_slice())?; - let key: RmrkString = key.to_vec().try_into().ok()?; - let value: RmrkString = value.decode_or_default(); + let key: Key = key.to_vec().try_into().ok()?; + let value: Value = value.decode_or_default(); - let property = RmrkThemeProperty { - key, - value - }; - - Some(property) + Some(mapper(key, value)) }); Ok(properties) --- a/pallets/proxy-rmrk-core/src/property.rs +++ b/pallets/proxy-rmrk-core/src/property.rs @@ -23,8 +23,8 @@ EquippableList, ZIndex, ThemeName, - ThemeProperty(&'r RmrkString), ThemeInherit, + UserProperty(&'r [u8]), } impl<'r> RmrkProperty<'r> { @@ -66,8 +66,8 @@ Self::EquippableList => key!("equippable-list"), Self::ZIndex => key!("z-index"), Self::ThemeName => key!("theme-name"), - Self::ThemeProperty(name) => key!("theme-property-", name), Self::ThemeInherit => key!("theme-inherit"), + Self::UserProperty(name) => key!("userprop-", name), } } } --- a/pallets/proxy-rmrk-equip/src/lib.rs +++ b/pallets/proxy-rmrk-equip/src/lib.rs @@ -185,7 +185,7 @@ token_id, PropertyScope::Rmrk, >::rmrk_property( - ThemeProperty(&property.key), + UserProperty(property.key.as_slice()), &property.value )? )?; --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -934,8 +934,9 @@ RmrkString, BoundedVec, >; -pub type RmrkPropertyInfo = - PropertyInfo, BoundedVec>; +pub type RmrkKeyString = BoundedVec; +pub type RmrkValueString = BoundedVec; +pub type RmrkPropertyInfo = PropertyInfo; pub type RmrkBaseInfo = BaseInfo; pub type RmrkPartType = PartType>; --- a/runtime/common/src/runtime_apis.rs +++ b/runtime/common/src/runtime_apis.rs @@ -232,78 +232,47 @@ } fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option>) -> Result, DispatchError> { - use pallet_proxy_rmrk_core::misc::RmrkDecode; + use pallet_proxy_rmrk_core::misc::CollectionType; let collection_id = CollectionId(collection_id); - if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); } - - let properties = Common::collection_properties(collection_id); - - // todo repeated code - return Ok(match filter_keys { - Some(keys) => { - let keys = Common::bytes_keys_to_property_keys(keys)?; - let properties = keys - .into_iter() - .filter_map(|key| { - properties.get(&key).map(|value| RmrkPropertyInfo { - key: key.decode_or_default(), - value: value.decode_or_default(), - }) - }) - .collect(); + if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { + return Ok(Vec::new()); + } - properties - } - None => { - properties - .into_iter() - .filter_map(|(key, value)| Some(RmrkPropertyInfo { - key: key.decode_or_default(), - value: value.decode_or_default(), - })) - .collect() + let properties = RmrkCore::filter_user_properties( + collection_id, + /* token_id = */ None, + filter_keys, + |key, value| RmrkPropertyInfo { + key, + value } - }); + )?; + + Ok(properties) } fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option>) -> Result, DispatchError> { - use frame_support::BoundedVec; - use pallet_proxy_rmrk_core::misc::RmrkDecode; + use pallet_proxy_rmrk_core::misc::NftType; let collection_id = CollectionId(collection_id); let token_id = TokenId(nft_id); - if !RmrkCore::nft_exists(collection_id, token_id) { return Ok(Vec::new()); } - let properties = Nonfungible::token_properties((collection_id, token_id)); - // todo look into this usage of pallet_nonfungible + if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() { + return Ok(Vec::new()); + } - // todo displace to a function? redundant code piece with collection props - return Ok(match filter_keys { - Some(keys) => { - let keys = Common::bytes_keys_to_property_keys(keys)?; - let properties = keys - .into_iter() - .filter_map(|key| { - properties.get(&key).map(|value| RmrkPropertyInfo { - key: key.decode_or_default(), - value: value.decode_or_default(), - }) - }) - .collect(); + let properties = RmrkCore::filter_user_properties( + collection_id, + Some(token_id), + filter_keys, + |key, value| RmrkPropertyInfo { + key, + value + } + )?; - properties - } - None => { - properties - .into_iter() - .filter_map(|(key, value)| Some(RmrkPropertyInfo { - key: key.decode_or_default(), - value: value.decode_or_default(), - })) - .collect() - } - }); + Ok(properties) } fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result, DispatchError> { @@ -436,7 +405,15 @@ None => return Ok(None) }; - let properties = RmrkCore::filter_theme_properties(collection_id, theme_id, filter_keys)?; + let properties = RmrkCore::filter_user_properties( + collection_id, + Some(theme_id), + filter_keys, + |key, value| RmrkThemeProperty { + key, + value + } + )?; let inherit = RmrkCore::get_nft_property( collection_id, -- gitstuff