difftreelog
feat add rmrk proxy nft minting
in: master
5 files changed
pallets/nonfungible/src/lib.rsdiffbeforeafterboth22use up_data_structs::{22use up_data_structs::{23 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,23 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,24 mapping::TokenAddressMapping, NestingRule, budget::Budget, Property, PropertyPermission,24 mapping::TokenAddressMapping, NestingRule, budget::Budget, Property, PropertyPermission,25 PropertyKey, PropertyKeyPermission, Properties, TrySetProperty,25 PropertyKey, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty,26};26};27use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};27use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};28use pallet_common::{28use pallet_common::{194 <TokenData<T>>::contains_key((collection.id, token))194 <TokenData<T>>::contains_key((collection.id, token))195 }195 }196197 pub fn set_scoped_token_property(198 collection: &CollectionHandle<T>,199 token_id: TokenId,200 scope: PropertyScope,201 property: Property,202 ) -> DispatchResult {203 TokenProperties::<T>::try_mutate((collection.id, token_id), |properties| {204 properties.try_scoped_set(scope, property.key, property.value)205 })206 .map_err(<CommonError<T>>::from)?;207208 Ok(())209 }210211 pub fn set_scoped_token_properties(212 collection: &CollectionHandle<T>,213 token_id: TokenId,214 scope: PropertyScope,215 properties: impl Iterator<Item=Property>,216 ) -> DispatchResult {217 TokenProperties::<T>::try_mutate((collection.id, token_id), |stored_properties| {218 stored_properties.try_scoped_set_from_iter(scope, properties)219 })220 .map_err(<CommonError<T>>::from)?;221222 Ok(())223 }224225 pub fn current_token_id(collection: &CollectionHandle<T>) -> TokenId {226 TokenId(<TokensMinted<T>>::get(collection.id))227 }196}228}197229198// unchecked calls skips any permission checks230// unchecked calls skips any permission checkspallets/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.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);
pallets/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(