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.rsdiffbeforeafterboth181819use 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 }737974 #[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,808781 /* 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 }869687 #[pallet::call]97 #[pallet::call]147157148 let unique_collection_id = collection_id.into();158 let unique_collection_id = collection_id.into();149159150 let collection = Self::get_nft_collection(unique_collection_id)?;160 let collection = Self::get_typed_nft_collection(unique_collection_id, CollectionType::Regular)?;151152 Self::check_collection_type(unique_collection_id, CollectionType::Regular)?;153161154 ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);162 ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);155163196 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());198206199 let collection = Self::get_nft_collection(collection_id.into())?;207 let collection = Self::get_typed_nft_collection(208 collection_id.into(),209 CollectionType::Regular210 )?;200 collection.check_is_owner(&cross_sender)?;211 collection.check_is_owner(&cross_sender)?;201212210 Ok(())221 Ok(())211 }222 }223224 #[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());237238 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {239 recipient: recipient.unwrap_or_else(|| owner.clone()),240 amount241 });242243 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 )?;257258 Self::deposit_event(Event::NftMinted {259 owner,260 collection_id,261 nft_id: nft_id.0262 });263264 Ok(())265 }212 }266 }213}267}214268215impl<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_type281 )?;282283 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 };290291 let budget = budget::Value::new(2);292293 <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 => CollectionFullOrLocked303 }304 )305 })?;306307 let nft_id = <PalletNft<T>>::current_token_id(&collection);308309 <PalletNft<T>>::set_scoped_token_properties(310 &collection,311 nft_id,312 PropertyScope::Rmrk,313 properties314 )?;315316 Ok(nft_id)317 }318216 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_type328 )?.into_inner();223 collection.check_is_owner(&T::CrossAccountId::from_sub(sender))?;329 collection.check_is_owner(&T::CrossAccountId::from_sub(sender))?;224225 Self::check_collection_type(collection_id, collection_type)?;226330227 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();262366263 Ok(nft_property)367 Ok(nft_property)270 Ok(())374 Ok(())271 }375 }376377 fn get_typed_nft_collection(378 collection_id: CollectionId,379 collection_type: CollectionType380 ) -> Result<NonfungibleHandle<T>, DispatchError> {381 Self::check_collection_type(collection_id, collection_type)?;382383 Self::get_nft_collection(collection_id)384 }272}385}273386pallets/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(