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.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.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.rsdiffbeforeafterboth4pub enum RmrkProperty {4pub enum RmrkProperty {5 Metadata,5 Metadata,6 CollectionType,6 CollectionType,7 Recipient,8 Royalty,7 RoyaltyInfo,9 Equipped,8 Equipped,10 ResourceCollection,9 ResourceCollection,11 ResourcePriorities,10 ResourcePriorities,48 match self {47 match self {49 Self::Metadata => key!("metadata"),48 Self::Metadata => key!("metadata"),50 Self::CollectionType => key!("collection-type"),49 Self::CollectionType => key!("collection-type"),51 Self::Recipient => key!("recipient"),52 Self::Royalty => key!("royalty"),50 Self::RoyaltyInfo => key!("royalty-info"),53 Self::Equipped => key!("equipped"),51 Self::Equipped => key!("equipped"),54 Self::ResourceCollection => key!("resource-collection"),52 Self::ResourceCollection => key!("resource-collection"),55 Self::ResourcePriorities => key!("resource-priorities"),53 Self::ResourcePriorities => key!("resource-priorities"),runtime/common/src/runtime_apis.rsdiffbeforeafterboth188 };188 };189189190 let keys = [190 let keys = [191 RmrkProperty::Royalty,191 RmrkProperty::RoyaltyInfo,192 RmrkProperty::Metadata,192 RmrkProperty::Metadata,193 RmrkProperty::Equipped,193 RmrkProperty::Equipped,194 RmrkProperty::Pending,195 // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"194 // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"196 ];195 ];197196315 let collection_id = CollectionId(collection_id);314 let collection_id = CollectionId(collection_id);316 let nft_id = TokenId(nft_id);315 let nft_id = TokenId(nft_id);317316318 let keys = [317 // let keys = [319 RmrkProperty::Royalty,318 // RmrkProperty::Royalty,320 RmrkProperty::Metadata,319 // RmrkProperty::Metadata,321 RmrkProperty::Equipped,320 // RmrkProperty::Equipped,322 RmrkProperty::Pending,321 // RmrkProperty::Pending,323 // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"322 // // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"324 ];323 // ];325324326 /*let resources = keys.into_iter().map(325 /*let resources = keys.into_iter().map(327 |key| BoundedVec::try_from(326 |key| BoundedVec::try_from(