git.delta.rocks / unique-network / refs/commits / d15bc44d3ad9

difftreelog

feat add rmrk proxy nft minting

Daniel Shiposha2022-05-23parent: #c22e2cd.patch.diff
in: master

5 files changed

modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -22,7 +22,7 @@
 use up_data_structs::{
 	AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
 	mapping::TokenAddressMapping, NestingRule, budget::Budget, Property, PropertyPermission,
-	PropertyKey, PropertyKeyPermission, Properties, TrySetProperty,
+	PropertyKey, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty,
 };
 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
 use pallet_common::{
@@ -193,6 +193,38 @@
 	pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {
 		<TokenData<T>>::contains_key((collection.id, token))
 	}
+
+	pub fn set_scoped_token_property(
+		collection: &CollectionHandle<T>,
+		token_id: TokenId,
+		scope: PropertyScope,
+		property: Property,
+	) -> DispatchResult {
+		TokenProperties::<T>::try_mutate((collection.id, token_id), |properties| {
+			properties.try_scoped_set(scope, property.key, property.value)
+		})
+		.map_err(<CommonError<T>>::from)?;
+
+		Ok(())
+	}
+
+	pub fn set_scoped_token_properties(
+		collection: &CollectionHandle<T>,
+		token_id: TokenId,
+		scope: PropertyScope,
+		properties: impl Iterator<Item=Property>,
+	) -> DispatchResult {
+		TokenProperties::<T>::try_mutate((collection.id, token_id), |stored_properties| {
+			stored_properties.try_scoped_set_from_iter(scope, properties)
+		})
+		.map_err(<CommonError<T>>::from)?;
+
+		Ok(())
+	}
+
+	pub fn current_token_id(collection: &CollectionHandle<T>) -> TokenId {
+		TokenId(<TokensMinted<T>>::get(collection.id))
+	}
 }
 
 // unchecked calls skips any permission checks
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
1818
19use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};19use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
20use frame_system::{pallet_prelude::*, ensure_signed};20use frame_system::{pallet_prelude::*, ensure_signed};
21use sp_runtime::{DispatchError, traits::StaticLookup};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
22use sp_std::vec::Vec;
22use up_data_structs::*;23use up_data_structs::*;
23use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};24use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};
24use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};25use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};
69 issuer: T::AccountId,70 issuer: T::AccountId,
70 collection_id: RmrkCollectionId,71 collection_id: RmrkCollectionId,
71 },72 },
73 NftMinted {
74 owner: T::AccountId,
75 collection_id: RmrkCollectionId,
76 nft_id: RmrkNftId,
77 },
72 }78 }
7379
74 #[pallet::error]80 #[pallet::error]
75 pub enum Error<T> {81 pub enum Error<T> {
76 /* Unique-specific events */82 /* Unique-specific events */
77 CorruptedCollectionType,83 CorruptedCollectionType,
84 NftTypeEncodeError,
78 RmrkPropertyKeyIsTooLong,85 RmrkPropertyKeyIsTooLong,
79 RmrkPropertyValueIsTooLong,86 RmrkPropertyValueIsTooLong,
8087
81 /* RMRK compatible events */88 /* RMRK compatible events */
82 CollectionNotEmpty,89 CollectionNotEmpty,
83 NoAvailableCollectionId,90 NoAvailableCollectionId,
91 NoAvailableNftId,
84 CollectionUnknown,92 CollectionUnknown,
93 NoPermission,
94 CollectionFullOrLocked,
85 }95 }
8696
87 #[pallet::call]97 #[pallet::call]
147157
148 let unique_collection_id = collection_id.into();158 let unique_collection_id = collection_id.into();
149159
150 let collection = Self::get_nft_collection(unique_collection_id)?;160 let collection = Self::get_typed_nft_collection(unique_collection_id, CollectionType::Regular)?;
151
152 Self::check_collection_type(unique_collection_id, CollectionType::Regular)?;
153161
154 ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);162 ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);
155163
196 let sender = ensure_signed(origin)?;204 let sender = ensure_signed(origin)?;
197 let cross_sender = T::CrossAccountId::from_sub(sender.clone());205 let cross_sender = T::CrossAccountId::from_sub(sender.clone());
198206
199 let collection = Self::get_nft_collection(collection_id.into())?;207 let collection = Self::get_typed_nft_collection(
208 collection_id.into(),
209 CollectionType::Regular
210 )?;
200 collection.check_is_owner(&cross_sender)?;211 collection.check_is_owner(&cross_sender)?;
201212
210 Ok(())221 Ok(())
211 }222 }
223
224 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
225 #[transactional]
226 pub fn mint_nft(
227 origin: OriginFor<T>,
228 owner: T::AccountId,
229 collection_id: RmrkCollectionId,
230 recipient: Option<T::AccountId>,
231 royalty_amount: Option<Permill>,
232 metadata: RmrkString,
233 ) -> DispatchResult {
234 let sender = ensure_signed(origin)?;
235 let sender = T::CrossAccountId::from_sub(sender);
236 let cross_owner = T::CrossAccountId::from_sub(owner.clone());
237
238 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {
239 recipient: recipient.unwrap_or_else(|| owner.clone()),
240 amount
241 });
242
243 let nft_id = Self::create_nft(
244 sender,
245 cross_owner,
246 collection_id.into(),
247 CollectionType::Regular,
248 NftType::Regular,
249 [
250 rmrk_property!(Config=T, RoyaltyInfo: royalty_info)?,
251 rmrk_property!(Config=T, Metadata: metadata)?,
252 rmrk_property!(Config=T, Equipped: false)?,
253 rmrk_property!(Config=T, ResourceCollection: None::<CollectionId>)?,
254 rmrk_property!(Config=T, ResourcePriorities: <Vec<u8>>::new())?,
255 ].into_iter()
256 )?;
257
258 Self::deposit_event(Event::NftMinted {
259 owner,
260 collection_id,
261 nft_id: nft_id.0
262 });
263
264 Ok(())
265 }
212 }266 }
213}267}
214268
215impl<T: Config> Pallet<T> {269impl<T: Config> Pallet<T> {
270 fn create_nft(
271 sender: T::CrossAccountId,
272 owner: T::CrossAccountId,
273 collection_id: CollectionId,
274 collection_type: CollectionType,
275 nft_type: NftType,
276 properties: impl Iterator<Item=Property>
277 ) -> Result<TokenId, DispatchError> {
278 let collection = Self::get_typed_nft_collection(
279 collection_id,
280 collection_type
281 )?;
282
283 let data = CreateNftExData {
284 const_data: nft_type.encode()
285 .try_into()
286 .map_err(|_| <Error<T>>::NftTypeEncodeError)?,
287 properties: BoundedVec::default(),
288 owner,
289 };
290
291 let budget = budget::Value::new(2);
292
293 <PalletNft<T>>::create_item(
294 &collection,
295 &sender,
296 data,
297 &budget,
298 ).map_err(|err| {
299 map_common_err_to_proxy!(
300 match err {
301 NoPermission => NoPermission,
302 CollectionTokenLimitExceeded => CollectionFullOrLocked
303 }
304 )
305 })?;
306
307 let nft_id = <PalletNft<T>>::current_token_id(&collection);
308
309 <PalletNft<T>>::set_scoped_token_properties(
310 &collection,
311 nft_id,
312 PropertyScope::Rmrk,
313 properties
314 )?;
315
316 Ok(nft_id)
317 }
318
216 fn change_collection_owner(319 fn change_collection_owner(
217 collection_id: CollectionId,320 collection_id: CollectionId,
218 collection_type: CollectionType,321 collection_type: CollectionType,
219 sender: T::AccountId,322 sender: T::AccountId,
220 new_owner: T::AccountId,323 new_owner: T::AccountId,
221 ) -> DispatchResult {324 ) -> DispatchResult {
222 let mut collection = Self::get_nft_collection(collection_id)?.into_inner();325 let mut collection = Self::get_typed_nft_collection(
326 collection_id,
327 collection_type
328 )?.into_inner();
223 collection.check_is_owner(&T::CrossAccountId::from_sub(sender))?;329 collection.check_is_owner(&T::CrossAccountId::from_sub(sender))?;
224
225 Self::check_collection_type(collection_id, collection_type)?;
226330
227 collection.owner = new_owner;331 collection.owner = new_owner;
228 collection.save()332 collection.save()
257 pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {361 pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
258 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))362 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
259 .get(&rmrk_property!(Config=T, key)?)363 .get(&rmrk_property!(Config=T, key)?)
260 .ok_or(<Error<T>>::CollectionUnknown)? // todo is that right?364 .ok_or(<Error<T>>::NoAvailableNftId)?
261 .clone();365 .clone();
262366
263 Ok(nft_property)367 Ok(nft_property)
270 Ok(())374 Ok(())
271 }375 }
376
377 fn get_typed_nft_collection(
378 collection_id: CollectionId,
379 collection_type: CollectionType
380 ) -> Result<NonfungibleHandle<T>, DispatchError> {
381 Self::check_collection_type(collection_id, collection_type)?;
382
383 Self::get_nft_collection(collection_id)
384 }
272}385}
273386
modifiedpallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/misc.rs
+++ b/pallets/proxy-rmrk-core/src/misc.rs
@@ -1,17 +1,9 @@
 use super::*;
 use codec::{Encode, Decode};
-use pallet_nonfungible::NonfungibleHandle;
+use pallet_nonfungible::{NonfungibleHandle, ItemData};
 
 macro_rules! impl_rmrk_value {
     ($enum_name:path, decode_error: $error:ident) => {
-        impl IntoPropertyValue for $enum_name {
-            fn into_property_value(self) -> Result<PropertyValue, MiscError> {
-                self.encode()
-                    .try_into()
-                    .map_err(|_| MiscError::RmrkPropertyValueIsTooLong)
-            }
-        }
-
         impl TryFrom<&PropertyValue> for $enum_name {
             type Error = MiscError;
 
@@ -26,6 +18,19 @@
     };
 }
 
+#[macro_export]
+macro_rules! map_common_err_to_proxy {
+    (match $err:ident { $($common_err:ident => $proxy_err:ident),+ }) => {
+        $(
+            if $err == <CommonError<T>>::$common_err.into() {
+                return <Error<T>>::$proxy_err.into()
+            } else
+        )+ {
+            $err
+        }
+    };
+}
+
 pub enum MiscError {
     RmrkPropertyValueIsTooLong,
     CorruptedCollectionType,
@@ -57,14 +62,26 @@
     fn into_property_value(self) -> Result<PropertyValue, MiscError>;
 }
 
-impl<L: Get<u32>> IntoPropertyValue for BoundedVec<u8, L> {
+impl<T: Encode> IntoPropertyValue for T {
     fn into_property_value(self) -> Result<PropertyValue, MiscError> {
-        self.into_inner()
+        self.encode()
             .try_into()
             .map_err(|_| MiscError::RmrkPropertyValueIsTooLong)
     }
 }
 
+pub trait RmrkNft {
+    fn rmrk_nft_type(&self) -> Option<NftType>;
+}
+
+impl<CrossAccountId> RmrkNft for ItemData<CrossAccountId> {
+    fn rmrk_nft_type(&self) -> Option<NftType> {
+        let mut value = self.const_data.as_slice();
+
+        NftType::decode(&mut value).ok()
+    }
+}
+
 #[derive(Encode, Decode, PartialEq, Eq)]
 pub enum CollectionType {
     Regular,
@@ -72,4 +89,13 @@
     Base,
 }
 
+#[derive(Encode, Decode, PartialEq, Eq)]
+pub enum NftType {
+    Regular,
+    Resource,
+    FixedPart,
+    SlotPart,
+    Theme
+}
+
 impl_rmrk_value!(CollectionType, decode_error: CorruptedCollectionType);
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/property.rs
+++ b/pallets/proxy-rmrk-core/src/property.rs
@@ -4,8 +4,7 @@
 pub enum RmrkProperty {
     Metadata,
     CollectionType,
-    Recipient,
-    Royalty,
+    RoyaltyInfo,
     Equipped,
     ResourceCollection,
     ResourcePriorities,
@@ -48,8 +47,7 @@
         match self {
             Self::Metadata => key!("metadata"),
             Self::CollectionType => key!("collection-type"),
-            Self::Recipient => key!("recipient"),
-            Self::Royalty => key!("royalty"),
+            Self::RoyaltyInfo => key!("royalty-info"),
             Self::Equipped => key!("equipped"),
             Self::ResourceCollection => key!("resource-collection"),
             Self::ResourcePriorities => key!("resource-priorities"),
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -188,10 +188,9 @@
                     };
 
                     let keys = [
-                        RmrkProperty::Royalty,
+                        RmrkProperty::RoyaltyInfo,
                         RmrkProperty::Metadata,
                         RmrkProperty::Equipped,
-                        RmrkProperty::Pending,
                         // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
                     ];
 
@@ -315,13 +314,13 @@
                     let collection_id = CollectionId(collection_id);
                     let nft_id = TokenId(nft_id);
 
-                    let keys = [
-                        RmrkProperty::Royalty,
-                        RmrkProperty::Metadata,
-                        RmrkProperty::Equipped,
-                        RmrkProperty::Pending,
-                        // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
-                    ];
+                    // let keys = [
+                    //     RmrkProperty::Royalty,
+                    //     RmrkProperty::Metadata,
+                    //     RmrkProperty::Equipped,
+                    //     RmrkProperty::Pending,
+                    //     // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
+                    // ];
 
                     /*let resources = keys.into_iter().map(
                         |key| BoundedVec::try_from(