From a66e7a4819dca7c31edf03464eba02273b42cffd Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 05 May 2022 13:08:48 +0000 Subject: [PATCH] Add properties RPC --- --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -19,7 +19,10 @@ use codec::Decode; use jsonrpc_core::{Error as RpcError, ErrorCode, Result}; use jsonrpc_derive::rpc; -use up_data_structs::{RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId}; +use up_data_structs::{ + RpcCollection, CollectionId, CollectionStats, CollectionLimits, TokenId, Property, PropertyKey, + PropertyKeyPermission, +}; use sp_api::{BlockId, BlockT, ProvideRuntimeApi, ApiExt}; use sp_blockchain::HeaderBackend; use up_rpc::UniqueApi as UniqueRuntimeApi; @@ -76,6 +79,31 @@ at: Option, ) -> Result>; + #[rpc(name = "unique_collectionProperties")] + fn collection_properties( + &self, + collection: CollectionId, + keys: Vec, + at: Option, + ) -> Result>; + + #[rpc(name = "unique_tokenProperties")] + fn token_properties( + &self, + collection: CollectionId, + token_id: TokenId, + properties: Vec, + at: Option, + ) -> Result>; + + #[rpc(name = "unique_propertyPermissions")] + fn property_permissions( + &self, + collection: CollectionId, + keys: Vec, + at: Option, + ) -> Result>; + #[rpc(name = "unique_totalSupply")] fn total_supply(&self, collection: CollectionId, at: Option) -> Result; #[rpc(name = "unique_accountBalance")] @@ -177,7 +205,9 @@ macro_rules! pass_method { ( - $method_name:ident($($name:ident: $ty:ty),* $(,)?) -> $result:ty $(=> $mapper:expr)? + $method_name:ident( + $($(#[map(|$map_arg:ident| $map:expr)])? $name:ident: $ty:ty),* $(,)? + ) -> $result:ty $(=> $mapper:expr)? $(; changed_in $ver:expr, $changed_method_name:ident ($($changed_name:expr), * $(,)?) => $fixer:expr)* ) => { fn $method_name( @@ -205,7 +235,7 @@ let result = $(if _api_version < $ver { api.$changed_method_name(&at, $($changed_name),*).map(|r| r.map($fixer)) } else)* - { api.$method_name(&at, $($name),*) }; + { api.$method_name(&at, $($((|$map_arg: $ty| $map))? ($name)),*) }; let result = result.map_err(|e| RpcError { code: ErrorCode::ServerError(Error::RuntimeError.into()), @@ -242,6 +272,28 @@ pass_method!(const_metadata(collection: CollectionId, token: TokenId) -> Vec); pass_method!(variable_metadata(collection: CollectionId, token: TokenId) -> Vec); + pass_method!(collection_properties( + collection: CollectionId, + + #[map(|keys| string_keys_to_bytes_keys(keys))] + keys: Vec + ) -> Vec); + + pass_method!(token_properties( + collection: CollectionId, + token_id: TokenId, + + #[map(|keys| string_keys_to_bytes_keys(keys))] + properties: Vec + ) -> Vec); + + pass_method!(property_permissions( + collection: CollectionId, + + #[map(|keys| string_keys_to_bytes_keys(keys))] + keys: Vec + ) -> Vec); + pass_method!(total_supply(collection: CollectionId) -> u32); pass_method!(account_balance(collection: CollectionId, account: CrossAccountId) -> u32); pass_method!(balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> String => |v| v.to_string()); @@ -256,3 +308,7 @@ pass_method!(next_sponsored(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Option); pass_method!(effective_collection_limits(collection_id: CollectionId) -> Option); } + +fn string_keys_to_bytes_keys(keys: Vec) -> Vec> { + keys.into_iter().map(|key| key.into_bytes()).collect() +} --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -388,6 +388,7 @@ /// Collection properties #[pallet::storage] + #[pallet::getter(fn collection_properties)] pub type CollectionProperties = StorageMap< Hasher = Blake2_128Concat, Key = CollectionId, @@ -397,7 +398,7 @@ >; #[pallet::storage] - #[pallet::getter(fn property_permission)] + #[pallet::getter(fn property_permissions)] pub type CollectionPropertyPermissions = StorageMap< Hasher = Blake2_128Concat, Key = CollectionId, @@ -656,9 +657,7 @@ CollectionProperties::::insert( id, Properties::from_collection_props_vec(data.properties) - .map_err(|e| -> Error:: { - e.into() - })?, + .map_err(|e| -> Error { e.into() })?, ); let token_props_permissions: PropertiesPermissionMap = data @@ -667,9 +666,7 @@ .map(|property| (property.key, property.permission)) .collect::>() .try_into() - .map_err(|_| -> Error:: { - PropertiesError::PropertyLimitReached.into() - })?; + .map_err(|_| -> Error { PropertiesError::PropertyLimitReached.into() })?; CollectionPropertyPermissions::::insert(id, token_props_permissions); @@ -754,9 +751,7 @@ CollectionProperties::::try_mutate(collection.id, |properties| { properties.try_set_property(property.clone()) }) - .map_err(|e| -> Error:: { - e.into() - })?; + .map_err(|e| -> Error { e.into() })?; Self::deposit_event(Event::CollectionPropertySet(collection.id, property)); @@ -786,7 +781,10 @@ properties.remove_property(&property_key); }); - Self::deposit_event(Event::CollectionPropertyDeleted(collection.id, property_key)); + Self::deposit_event(Event::CollectionPropertyDeleted( + collection.id, + property_key, + )); Ok(()) } @@ -823,9 +821,7 @@ let property_permission = property_permission.clone(); permissions.try_insert(property_permission.key, property_permission.permission) }) - .map_err(|_| -> Error:: { - PropertiesError::PropertyLimitReached.into() - })?; + .map_err(|_| -> Error { PropertiesError::PropertyLimitReached.into() })?; Self::deposit_event(Event::PropertyPermissionSet( collection.id, --- a/pallets/nonfungible/src/common.rs +++ b/pallets/nonfungible/src/common.rs @@ -184,7 +184,7 @@ with_weight( >::delete_collection_properties(self, &sender, property_keys), - weight + weight, ) } --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -96,6 +96,7 @@ >; #[pallet::storage] + #[pallet::getter(fn token_properties)] pub type TokenProperties = StorageNMap< Key = (Key, Key), Value = up_data_structs::Properties, @@ -265,9 +266,8 @@ >::try_mutate((collection.id, token_id), |properties| { properties.try_set_property(property.clone()) - }).map_err(|e| -> CommonError:: { - e.into() - })?; + }) + .map_err(|e| -> CommonError { e.into() })?; >::deposit_event(CommonEvent::TokenPropertySet( collection.id, @@ -318,7 +318,7 @@ token_id: TokenId, property_key: &PropertyKey, ) -> DispatchResult { - let permission = >::property_permission(collection.id) + let permission = >::property_permissions(collection.id) .get(property_key) .map(|p| p.clone()) .unwrap_or(PropertyPermission::None); --- a/primitives/data-structs/src/lib.rs +++ b/primitives/data-structs/src/lib.rs @@ -634,6 +634,7 @@ pub type PropertyValue = BoundedVec>; #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub enum PropertyPermission { None, AdminConst, @@ -644,14 +645,21 @@ } #[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct Property { + #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + + #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub value: PropertyValue, } #[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)] +#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct PropertyKeyPermission { + #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))] pub key: PropertyKey, + pub permission: PropertyPermission, } @@ -723,6 +731,10 @@ pub fn get_property(&self, key: &PropertyKey) -> Option<&PropertyValue> { self.map.get(key) } + + pub fn iter(&self) -> impl Iterator { + self.map.iter() + } } pub struct CollectionProperties; --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -16,7 +16,10 @@ #![cfg_attr(not(feature = "std"), no_std)] -use up_data_structs::{CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits}; +use up_data_structs::{ + CollectionId, TokenId, RpcCollection, CollectionStats, CollectionLimits, Property, PropertyKey, + PropertyKeyPermission, +}; use sp_std::vec::Vec; use codec::Decode; use sp_runtime::DispatchError; @@ -41,6 +44,19 @@ fn const_metadata(collection: CollectionId, token: TokenId) -> Result>; fn variable_metadata(collection: CollectionId, token: TokenId) -> Result>; + fn collection_properties(collection: CollectionId, properties: Vec>) -> Result>; + + fn token_properties( + collection: CollectionId, + token_id: TokenId, + properties: Vec> + ) -> Result>; + + fn property_permissions( + collection: CollectionId, + properties: Vec> + ) -> Result>; + fn total_supply(collection: CollectionId) -> Result; fn account_balance(collection: CollectionId, account: CrossAccountId) -> Result; fn balance(collection: CollectionId, account: CrossAccountId, token: TokenId) -> Result; --- a/runtime/common/src/runtime_apis.rs +++ b/runtime/common/src/runtime_apis.rs @@ -7,6 +7,14 @@ $($custom_apis:tt)+ )? ) => { + fn bytes_keys_to_property_keys(keys: Vec>) -> Result, DispatchError> { + keys.into_iter() + .map(|key| -> Result { + key.try_into().map_err(|_| DispatchError::Other("Can't read property key")) + }) + .collect::, DispatchError>>() + } + impl_runtime_apis! { $($($custom_apis)+)? @@ -36,6 +44,76 @@ dispatch_unique_runtime!(collection.variable_metadata(token)) } + fn collection_properties( + collection: CollectionId, + keys: Vec> + ) -> Result, DispatchError> { + let keys = bytes_keys_to_property_keys(keys)?; + + let properties = pallet_common::Pallet::::collection_properties(collection); + + let properties = keys.into_iter() + .filter_map(|key| { + properties.get_property(&key) + .map(|value| { + Property { + key, + value: value.clone() + } + }) + }) + .collect(); + + Ok(properties) + } + + fn token_properties( + collection: CollectionId, + token_id: TokenId, + keys: Vec> + ) -> Result, DispatchError> { + let keys = bytes_keys_to_property_keys(keys)?; + + let properties = pallet_nonfungible::Pallet::::token_properties((collection, token_id)); + + let properties = keys.into_iter() + .filter_map(|key| { + properties.get_property(&key) + .map(|value| { + Property { + key, + value: value.clone() + } + }) + }) + .collect(); + + Ok(properties) + } + + fn property_permissions( + collection: CollectionId, + keys: Vec> + ) -> Result, DispatchError> { + let keys = bytes_keys_to_property_keys(keys)?; + + let permissions = pallet_common::Pallet::::property_permissions(collection); + + let key_permissions = keys.into_iter() + .filter_map(|key| { + permissions.get(&key) + .map(|permission| { + PropertyKeyPermission { + key, + permission: permission.clone() + } + }) + }) + .collect(); + + Ok(key_permissions) + } + fn total_supply(collection: CollectionId) -> Result { dispatch_unique_runtime!(collection.total_supply()) } --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -67,7 +67,7 @@ }, }; use up_data_structs::mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping}; -use up_data_structs::{CollectionId, TokenId, CollectionStats, CollectionLimits, RpcCollection}; +use up_data_structs::*; // use pallet_contracts::weights::WeightInfo; // #[cfg(any(feature = "std", test))] use frame_system::{ -- gitstuff