git.delta.rocks / unique-network / refs/commits / 5c5d937f9f7f

difftreelog

refactor iterate rmrk props, add rmrk proxy set_propertty

Daniel Shiposha2022-05-25parent: #aab4f30.patch.diff
in: master

5 files changed

modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- 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<RmrkNftId>,
+			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<T>,
+			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,
+			maybe_nft_id: Option<RmrkNftId>,
+			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)?;
+
+                    <PalletNft<T>>::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)?;
+
+                    <PalletCommon<T>>::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, <CommonError<T>>::NoPermission);
+        ensure!(actual_type == nft_type, <Error<T>>::NoPermission);
 
         Ok(())
     }
 
-    pub fn filter_theme_properties(
+    pub fn ensure_nft_owner(
         collection_id: CollectionId,
         token_id: TokenId,
-        filter_keys: Option<Vec<RmrkPropertyKey>>
-    ) -> Result<Vec<RmrkThemeProperty>, DispatchError> {
+        possible_owner: &T::CrossAccountId
+    ) -> DispatchResult {
+        let token_data = <TokenData<T>>::get((collection_id, token_id))
+            .ok_or(<Error<T>>::NoAvailableNftId)?;
+
+        ensure!(token_data.owner == *possible_owner, <Error<T>>::NoPermission);
+
+        Ok(())
+    }
+
+    pub fn filter_user_properties<Key, Value, R, Mapper>(
+        collection_id: CollectionId,
+        token_id: Option<TokenId>,
+        filter_keys: Option<Vec<RmrkPropertyKey>>,
+        mapper: Mapper,
+    ) -> Result<Vec<R>, DispatchError>
+    where
+        Key: TryFrom<RmrkPropertyKey> + 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<Key, Value, R, Mapper>(
         collection_id: CollectionId,
-        token_id: TokenId
-    ) -> Result<impl Iterator<Item=RmrkThemeProperty>, DispatchError> {
-        let key_prefix = Self::rmrk_property_key(ThemeProperty(&RmrkString::default()))?;
+        token_id: Option<TokenId>,
+        mapper: Mapper,
+    ) -> Result<impl Iterator<Item=R>, DispatchError>
+    where
+        Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,
+        Value: Decode + Default,
+        Mapper: Fn(Key, Value) -> R
+    {
+        let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;
 
-        let properties = <PalletNft<T>>::token_properties((collection_id, token_id))
+        let properties = match token_id {
+            Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),
+            None => <PalletCommon<T>>::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)
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
--- 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),
         }
     }
 }
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-equip/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::DispatchError;22use up_data_structs::*;23use pallet_common::{Pallet as PalletCommon, Error as CommonError};24use pallet_rmrk_core::{Pallet as PalletCore, misc::{self, *}, property::RmrkProperty::*};25use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};26use pallet_evm::account::CrossAccountId;2728pub use pallet::*;2930#[frame_support::pallet]31pub mod pallet {32    use super::*;3334	#[pallet::config]35	pub trait Config: frame_system::Config36                    + pallet_rmrk_core::Config {37		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;38	}3940    #[pallet::storage]41	#[pallet::getter(fn internal_part_id)]42	pub type InernalPartId<T: Config> = StorageDoubleMap<43        _,44        Twox64Concat,45        CollectionId,46        Twox64Concat,47        RmrkPartId,48        TokenId49    >;5051    #[pallet::storage]52	#[pallet::getter(fn base_has_default_theme)]53    pub type BaseHasDefaultTheme<T: Config> = StorageMap<54        _,55        Twox64Concat,56        CollectionId,57        bool,58        ValueQuery59    >;6061    #[pallet::pallet]62	#[pallet::generate_store(pub(super) trait Store)]63	pub struct Pallet<T>(_);6465	#[pallet::event]66	#[pallet::generate_deposit(pub(super) fn deposit_event)]67	pub enum Event<T: Config> {68        BaseCreated {69			issuer: T::AccountId,70			base_id: RmrkBaseId,71		},72    }7374    #[pallet::error]75	pub enum Error<T> {76        PermissionError,77        NoAvailableBaseId,78        NoAvailablePartId,79        BaseDoesntExist,80        NeedsDefaultThemeFirst,81    }8283    #[pallet::call]84	impl<T: Config> Pallet<T> {85        #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]86        #[transactional]87		pub fn create_base(88			origin: OriginFor<T>,89			base_type: RmrkString,90			symbol: RmrkString,91			parts: BoundedVec<RmrkPartType, RmrkPartsLimit>,92		) -> DispatchResult {93            let sender = ensure_signed(origin)?;94            let cross_sender = T::CrossAccountId::from_sub(sender.clone());9596            let data = CreateCollectionData {97                limits: None,98                token_prefix: symbol.into_inner()99                    .try_into()100                    .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,101                ..Default::default()102            };103104            let collection_id_res = <PalletNft<T>>::init_collection(sender.clone(), data);105106            if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {107                return Err(<Error<T>>::NoAvailableBaseId.into());108            }109110            let collection_id = collection_id_res?;111112            <PalletCommon<T>>::set_scoped_collection_properties(113                collection_id,114                PropertyScope::Rmrk,115                [116                    <PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,117                    <PalletCore<T>>::rmrk_property(BaseType, &base_type)?,118                ].into_iter()119            )?;120121            let collection = <PalletCore<T>>::get_nft_collection(collection_id)?;122123            for part in parts {124                let part_id = part.id();125                let part_token_id = Self::create_part(126                    &cross_sender,127                    &collection,128                    part129                )?;130131                <InernalPartId<T>>::insert(collection_id, part_id, part_token_id);132133                <PalletNft<T>>::set_scoped_token_property(134                    collection_id,135                    part_token_id,136                    PropertyScope::Rmrk,137                    <PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?138                )?;139            }140141            Self::deposit_event(Event::BaseCreated { issuer: sender, base_id: collection_id.0 });142143            Ok(())144        }145146        #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]147        #[transactional]148		pub fn theme_add(149			origin: OriginFor<T>,150			base_id: RmrkBaseId,151			theme: RmrkTheme,152		) -> DispatchResult {153            let sender = ensure_signed(origin)?;154155            let sender = T::CrossAccountId::from_sub(sender);156            let owner = &sender;157158            let collection_id: CollectionId = base_id.into();159160            let collection = <PalletCore<T>>::get_typed_nft_collection(161                collection_id,162                misc::CollectionType::Base163            ).map_err(|_| <Error<T>>::BaseDoesntExist)?;164165            if theme.name.as_slice() == b"default" {166                <BaseHasDefaultTheme<T>>::insert(collection_id, true);167            } else if !Self::base_has_default_theme(collection_id) {168                return Err(<Error<T>>::NeedsDefaultThemeFirst.into());169            }170171            let token_id = <PalletCore<T>>::create_nft(172                &sender,173                owner,174                &collection,175                NftType::Theme,176                [177                    <PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,178                    <PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?179                ].into_iter()180            ).map_err(|_| <Error<T>>::PermissionError)?;181182            for property in theme.properties {183                <PalletNft<T>>::set_scoped_token_property(184                    collection_id,185                    token_id,186                    PropertyScope::Rmrk,187                    <PalletCore<T>>::rmrk_property(188                        ThemeProperty(&property.key),189                        &property.value190                    )?191                )?;192            }193194            Ok(())195        }196    }197}198199impl<T: Config> Pallet<T> {200    fn create_part(201        sender: &T::CrossAccountId,202        collection: &NonfungibleHandle<T>,203        part: RmrkPartType204    ) -> Result<TokenId, DispatchError> {205        let owner = sender;206207        let src = part.src();208        let z_index = part.z_index();209210        let nft_type = match part {211            RmrkPartType::FixedPart(_) => NftType::FixedPart,212            RmrkPartType::SlotPart(_) => NftType::SlotPart,213        };214215        let token_id = <PalletCore<T>>::create_nft(216            sender,217            owner,218            collection,219            nft_type,220            [221                <PalletCore<T>>::rmrk_property(Src, &src)?,222                <PalletCore<T>>::rmrk_property(ZIndex, &z_index)?223            ].into_iter()224        ).map_err(|err| match err {225            DispatchError::Arithmetic(_) => <Error<T>>::NoAvailablePartId.into(),226            err => err227        })?;228229        if let RmrkPartType::SlotPart(part) = part {230            <PalletNft<T>>::set_scoped_token_property(231                collection.id,232                token_id,233                PropertyScope::Rmrk,234                <PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?235            )?;236        }237238        Ok(token_id)239    }240}
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -934,8 +934,9 @@
 	RmrkString,
 	BoundedVec<RmrkPartId, RmrkPartsLimit>,
 >;
-pub type RmrkPropertyInfo =
-	PropertyInfo<BoundedVec<u8, RmrkKeyLimit>, BoundedVec<u8, RmrkValueLimit>>;
+pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;
+pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;
+pub type RmrkPropertyInfo = PropertyInfo<RmrkKeyString, RmrkValueString>;
 pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;
 pub type RmrkPartType =
 	PartType<RmrkString, BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>>;
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- 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<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, 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<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, 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<Vec<RmrkResourceInfo>, 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,