difftreelog
feat add rmrk proxy nft minting
in: master
5 files changed
pallets/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
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -18,7 +18,8 @@
use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
use frame_system::{pallet_prelude::*, ensure_signed};
-use sp_runtime::{DispatchError, traits::StaticLookup};
+use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
+use sp_std::vec::Vec;
use up_data_structs::*;
use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};
use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};
@@ -69,19 +70,28 @@
issuer: T::AccountId,
collection_id: RmrkCollectionId,
},
+ NftMinted {
+ owner: T::AccountId,
+ collection_id: RmrkCollectionId,
+ nft_id: RmrkNftId,
+ },
}
#[pallet::error]
pub enum Error<T> {
/* Unique-specific events */
CorruptedCollectionType,
+ NftTypeEncodeError,
RmrkPropertyKeyIsTooLong,
RmrkPropertyValueIsTooLong,
/* RMRK compatible events */
CollectionNotEmpty,
NoAvailableCollectionId,
+ NoAvailableNftId,
CollectionUnknown,
+ NoPermission,
+ CollectionFullOrLocked,
}
#[pallet::call]
@@ -147,9 +157,7 @@
let unique_collection_id = collection_id.into();
- let collection = Self::get_nft_collection(unique_collection_id)?;
-
- Self::check_collection_type(unique_collection_id, CollectionType::Regular)?;
+ let collection = Self::get_typed_nft_collection(unique_collection_id, CollectionType::Regular)?;
ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);
@@ -196,7 +204,10 @@
let sender = ensure_signed(origin)?;
let cross_sender = T::CrossAccountId::from_sub(sender.clone());
- let collection = Self::get_nft_collection(collection_id.into())?;
+ let collection = Self::get_typed_nft_collection(
+ collection_id.into(),
+ CollectionType::Regular
+ )?;
collection.check_is_owner(&cross_sender)?;
let token_count = collection.total_supply();
@@ -209,20 +220,113 @@
Ok(())
}
+
+ #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
+ #[transactional]
+ pub fn mint_nft(
+ origin: OriginFor<T>,
+ owner: T::AccountId,
+ collection_id: RmrkCollectionId,
+ recipient: Option<T::AccountId>,
+ royalty_amount: Option<Permill>,
+ metadata: RmrkString,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin)?;
+ let sender = T::CrossAccountId::from_sub(sender);
+ let cross_owner = T::CrossAccountId::from_sub(owner.clone());
+
+ let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {
+ recipient: recipient.unwrap_or_else(|| owner.clone()),
+ amount
+ });
+
+ let nft_id = Self::create_nft(
+ sender,
+ cross_owner,
+ collection_id.into(),
+ CollectionType::Regular,
+ NftType::Regular,
+ [
+ rmrk_property!(Config=T, RoyaltyInfo: royalty_info)?,
+ rmrk_property!(Config=T, Metadata: metadata)?,
+ rmrk_property!(Config=T, Equipped: false)?,
+ rmrk_property!(Config=T, ResourceCollection: None::<CollectionId>)?,
+ rmrk_property!(Config=T, ResourcePriorities: <Vec<u8>>::new())?,
+ ].into_iter()
+ )?;
+
+ Self::deposit_event(Event::NftMinted {
+ owner,
+ collection_id,
+ nft_id: nft_id.0
+ });
+
+ Ok(())
+ }
}
}
impl<T: Config> Pallet<T> {
+ fn create_nft(
+ sender: T::CrossAccountId,
+ owner: T::CrossAccountId,
+ collection_id: CollectionId,
+ collection_type: CollectionType,
+ nft_type: NftType,
+ properties: impl Iterator<Item=Property>
+ ) -> Result<TokenId, DispatchError> {
+ let collection = Self::get_typed_nft_collection(
+ collection_id,
+ collection_type
+ )?;
+
+ let data = CreateNftExData {
+ const_data: nft_type.encode()
+ .try_into()
+ .map_err(|_| <Error<T>>::NftTypeEncodeError)?,
+ properties: BoundedVec::default(),
+ owner,
+ };
+
+ let budget = budget::Value::new(2);
+
+ <PalletNft<T>>::create_item(
+ &collection,
+ &sender,
+ data,
+ &budget,
+ ).map_err(|err| {
+ map_common_err_to_proxy!(
+ match err {
+ NoPermission => NoPermission,
+ CollectionTokenLimitExceeded => CollectionFullOrLocked
+ }
+ )
+ })?;
+
+ let nft_id = <PalletNft<T>>::current_token_id(&collection);
+
+ <PalletNft<T>>::set_scoped_token_properties(
+ &collection,
+ nft_id,
+ PropertyScope::Rmrk,
+ properties
+ )?;
+
+ Ok(nft_id)
+ }
+
fn change_collection_owner(
collection_id: CollectionId,
collection_type: CollectionType,
sender: T::AccountId,
new_owner: T::AccountId,
) -> DispatchResult {
- let mut collection = Self::get_nft_collection(collection_id)?.into_inner();
+ let mut collection = Self::get_typed_nft_collection(
+ collection_id,
+ collection_type
+ )?.into_inner();
collection.check_is_owner(&T::CrossAccountId::from_sub(sender))?;
-
- Self::check_collection_type(collection_id, collection_type)?;
collection.owner = new_owner;
collection.save()
@@ -257,7 +361,7 @@
pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
.get(&rmrk_property!(Config=T, key)?)
- .ok_or(<Error<T>>::CollectionUnknown)? // todo is that right?
+ .ok_or(<Error<T>>::NoAvailableNftId)?
.clone();
Ok(nft_property)
@@ -269,4 +373,13 @@
Ok(())
}
+
+ fn get_typed_nft_collection(
+ collection_id: CollectionId,
+ collection_type: CollectionType
+ ) -> Result<NonfungibleHandle<T>, DispatchError> {
+ Self::check_collection_type(collection_id, collection_type)?;
+
+ Self::get_nft_collection(collection_id)
+ }
}
pallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth1use super::*;1use super::*;2use codec::{Encode, Decode};2use codec::{Encode, Decode};3use pallet_nonfungible::NonfungibleHandle;3use pallet_nonfungible::{NonfungibleHandle, ItemData};445macro_rules! impl_rmrk_value {5macro_rules! impl_rmrk_value {6 ($enum_name:path, decode_error: $error:ident) => {6 ($enum_name:path, decode_error: $error:ident) => {7 impl IntoPropertyValue for $enum_name {8 fn into_property_value(self) -> Result<PropertyValue, MiscError> {9 self.encode()10 .try_into()11 .map_err(|_| MiscError::RmrkPropertyValueIsTooLong)12 }13 }1415 impl TryFrom<&PropertyValue> for $enum_name {7 impl TryFrom<&PropertyValue> for $enum_name {16 type Error = MiscError;8 type Error = MiscError;26 };18 };27}19}2021#[macro_export]22macro_rules! map_common_err_to_proxy {23 (match $err:ident { $($common_err:ident => $proxy_err:ident),+ }) => {24 $(25 if $err == <CommonError<T>>::$common_err.into() {26 return <Error<T>>::$proxy_err.into()27 } else28 )+ {29 $err30 }31 };32}283329pub enum MiscError {34pub enum MiscError {30 RmrkPropertyValueIsTooLong,35 RmrkPropertyValueIsTooLong,57 fn into_property_value(self) -> Result<PropertyValue, MiscError>;62 fn into_property_value(self) -> Result<PropertyValue, MiscError>;58}63}596460impl<L: Get<u32>> IntoPropertyValue for BoundedVec<u8, L> {65impl<T: Encode> IntoPropertyValue for T {61 fn into_property_value(self) -> Result<PropertyValue, MiscError> {66 fn into_property_value(self) -> Result<PropertyValue, MiscError> {62 self.into_inner()67 self.encode()63 .try_into()68 .try_into()64 .map_err(|_| MiscError::RmrkPropertyValueIsTooLong)69 .map_err(|_| MiscError::RmrkPropertyValueIsTooLong)65 }70 }66}71}7273pub trait RmrkNft {74 fn rmrk_nft_type(&self) -> Option<NftType>;75}7677impl<CrossAccountId> RmrkNft for ItemData<CrossAccountId> {78 fn rmrk_nft_type(&self) -> Option<NftType> {79 let mut value = self.const_data.as_slice();8081 NftType::decode(&mut value).ok()82 }83}678468#[derive(Encode, Decode, PartialEq, Eq)]85#[derive(Encode, Decode, PartialEq, Eq)]69pub enum CollectionType {86pub enum CollectionType {72 Base,89 Base,73}90}9192#[derive(Encode, Decode, PartialEq, Eq)]93pub enum NftType {94 Regular,95 Resource,96 FixedPart,97 SlotPart,98 Theme99}7410075impl_rmrk_value!(CollectionType, decode_error: CorruptedCollectionType);101impl_rmrk_value!(CollectionType, decode_error: CorruptedCollectionType);76102pallets/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"),
runtime/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(