difftreelog
refactor(rmrk-rpc) merged mapped calls for collection and id into one
in: master
2 files changed
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::{*, mapping::TokenAddressMapping};24use pallet_common::{25 Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::Pallet as PalletStructure;29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46 use super::*;47 use pallet_evm::account;4849 #[pallet::config]50 pub trait Config:51 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52 {53 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54 }5556 #[pallet::storage]57 #[pallet::getter(fn collection_index)]58 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960 #[pallet::storage]61 pub type UniqueCollectionId<T: Config> =62 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364 #[pallet::storage]65 pub type RmrkInernalCollectionId<T: Config> =66 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768 #[pallet::pallet]69 #[pallet::generate_store(pub(super) trait Store)]70 pub struct Pallet<T>(_);7172 #[pallet::event]73 #[pallet::generate_deposit(pub(super) fn deposit_event)]74 pub enum Event<T: Config> {75 CollectionCreated {76 issuer: T::AccountId,77 collection_id: RmrkCollectionId,78 },79 CollectionDestroyed {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 IssuerChanged {84 old_issuer: T::AccountId,85 new_issuer: T::AccountId,86 collection_id: RmrkCollectionId,87 },88 CollectionLocked {89 issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 NftMinted {93 owner: T::AccountId,94 collection_id: RmrkCollectionId,95 nft_id: RmrkNftId,96 },97 NFTBurned {98 owner: T::AccountId,99 nft_id: RmrkNftId,100 },101 PropertySet {102 collection_id: RmrkCollectionId,103 maybe_nft_id: Option<RmrkNftId>,104 key: RmrkKeyString,105 value: RmrkValueString,106 },107 ResourceAdded {108 nft_id: RmrkNftId,109 resource_id: RmrkResourceId,110 },111 ResourceRemoval {112 nft_id: RmrkNftId,113 resource_id: RmrkResourceId,114 },115 }116117 #[pallet::error]118 pub enum Error<T> {119 /* Unique-specific events */120 CorruptedCollectionType,121 NftTypeEncodeError,122 RmrkPropertyKeyIsTooLong,123 RmrkPropertyValueIsTooLong,124125 /* RMRK compatible events */126 CollectionNotEmpty,127 NoAvailableCollectionId,128 NoAvailableNftId,129 CollectionUnknown,130 NoPermission,131 CollectionFullOrLocked,132 ResourceDoesntExist,133 }134135 #[pallet::call]136 impl<T: Config> Pallet<T> {137 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]138 #[transactional]139 pub fn create_collection(140 origin: OriginFor<T>,141 metadata: RmrkString,142 max: Option<u32>,143 symbol: RmrkCollectionSymbol,144 ) -> DispatchResult {145 let sender = ensure_signed(origin)?;146147 let limits = CollectionLimits {148 owner_can_transfer: Some(false),149 token_limit: max,150 ..Default::default()151 };152153 let data = CreateCollectionData {154 limits: Some(limits),155 token_prefix: symbol156 .into_inner()157 .try_into()158 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,159 permissions: Some(CollectionPermissions {160 nesting: Some(NestingRule::Owner),161 ..Default::default()162 }),163 ..Default::default()164 };165166 let unique_collection_id = Self::init_collection(167 T::CrossAccountId::from_sub(sender.clone()),168 data,169 [170 Self::rmrk_property(Metadata, &metadata)?,171 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,172 ]173 .into_iter(),174 )?;175 let rmrk_collection_id = <CollectionIndex<T>>::get();176177 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);178 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);179180 <CollectionIndex<T>>::mutate(|n| *n += 1);181182 Self::deposit_event(Event::CollectionCreated {183 issuer: sender,184 collection_id: rmrk_collection_id,185 });186187 Ok(())188 }189190 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]191 #[transactional]192 pub fn destroy_collection(193 origin: OriginFor<T>,194 collection_id: RmrkCollectionId,195 ) -> DispatchResult {196 let sender = ensure_signed(origin)?;197 let cross_sender = T::CrossAccountId::from_sub(sender.clone());198199 let collection = Self::get_typed_nft_collection(200 Self::unique_collection_id(collection_id)?,201 misc::CollectionType::Regular,202 )?;203204 ensure!(205 collection.total_supply() == 0,206 <Error<T>>::CollectionNotEmpty207 );208209 <PalletNft<T>>::destroy_collection(collection, &cross_sender)210 .map_err(Self::map_common_err_to_proxy)?;211212 Self::deposit_event(Event::CollectionDestroyed {213 issuer: sender,214 collection_id,215 });216217 Ok(())218 }219220 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]221 #[transactional]222 pub fn change_collection_issuer(223 origin: OriginFor<T>,224 collection_id: RmrkCollectionId,225 new_issuer: <T::Lookup as StaticLookup>::Source,226 ) -> DispatchResult {227 let sender = ensure_signed(origin)?;228229 let new_issuer = T::Lookup::lookup(new_issuer)?;230231 Self::change_collection_owner(232 Self::unique_collection_id(collection_id)?,233 misc::CollectionType::Regular,234 sender.clone(),235 new_issuer.clone(),236 )?;237238 Self::deposit_event(Event::IssuerChanged {239 old_issuer: sender,240 new_issuer,241 collection_id,242 });243244 Ok(())245 }246247 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]248 #[transactional]249 pub fn lock_collection(250 origin: OriginFor<T>,251 collection_id: RmrkCollectionId,252 ) -> DispatchResult {253 let sender = ensure_signed(origin)?;254 let cross_sender = T::CrossAccountId::from_sub(sender.clone());255256 let collection = Self::get_typed_nft_collection(257 Self::unique_collection_id(collection_id)?,258 misc::CollectionType::Regular,259 )?;260261 Self::check_collection_owner(&collection, &cross_sender)?;262263 let token_count = collection.total_supply();264265 let mut collection = collection.into_inner();266 collection.limits.token_limit = Some(token_count);267 collection.save()?;268269 Self::deposit_event(Event::CollectionLocked {270 issuer: sender,271 collection_id,272 });273274 Ok(())275 }276277 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]278 #[transactional]279 pub fn mint_nft(280 origin: OriginFor<T>,281 owner: T::AccountId,282 collection_id: RmrkCollectionId,283 recipient: Option<T::AccountId>,284 royalty_amount: Option<Permill>,285 metadata: RmrkString,286 ) -> DispatchResult {287 let sender = ensure_signed(origin)?;288 let sender = T::CrossAccountId::from_sub(sender);289 let cross_owner = T::CrossAccountId::from_sub(owner.clone());290291 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {292 recipient: recipient.unwrap_or_else(|| owner.clone()),293 amount,294 });295296 let collection = Self::get_typed_nft_collection(297 Self::unique_collection_id(collection_id)?,298 misc::CollectionType::Regular,299 )?;300301 let nft_id = Self::create_nft(302 &sender,303 &cross_owner,304 &collection,305 [306 Self::rmrk_property(TokenType, &NftType::Regular)?,307 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,308 Self::rmrk_property(Metadata, &metadata)?,309 Self::rmrk_property(Equipped, &false)?,310 Self::rmrk_property(311 ResourceCollection,312 &Self::init_collection(313 sender.clone(),314 CreateCollectionData {315 ..Default::default()316 },317 [Self::rmrk_property(318 CollectionType,319 &misc::CollectionType::Resource,320 )?]321 .into_iter(),322 )?,323 )?, // todo possibly add limits to the collection if rmrk warrants them324 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,325 ]326 .into_iter(),327 )328 .map_err(|err| match err {329 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),330 err => Self::map_common_err_to_proxy(err),331 })?;332333 Self::deposit_event(Event::NftMinted {334 owner,335 collection_id,336 nft_id: nft_id.0,337 });338339 Ok(())340 }341342 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]343 #[transactional]344 pub fn burn_nft(345 origin: OriginFor<T>,346 collection_id: RmrkCollectionId,347 nft_id: RmrkNftId,348 ) -> DispatchResult {349 let sender = ensure_signed(origin)?;350 let cross_sender = T::CrossAccountId::from_sub(sender.clone());351352 Self::destroy_nft(353 cross_sender,354 Self::unique_collection_id(collection_id)?,355 misc::CollectionType::Regular,356 nft_id.into(),357 )?;358359 Self::deposit_event(Event::NFTBurned {360 owner: sender,361 nft_id,362 });363364 Ok(())365 }366367 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]368 #[transactional]369 pub fn send(370 origin: OriginFor<T>,371 rmrk_collection_id: RmrkCollectionId,372 rmrk_nft_id: RmrkNftId,373 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,374 ) -> DispatchResult {375 let sender = ensure_signed(origin.clone())?;376 let cross_sender = T::CrossAccountId::from_sub(sender.clone());377378 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;379 let nft_id = rmrk_nft_id.into();380381 let token_data = <TokenData<T>>::get((collection_id, nft_id))382 .ok_or(<Error<T>>::NoAvailableNftId)?;383384 let from = token_data.owner;385386 let collection = Self::get_typed_nft_collection(387 collection_id,388 misc::CollectionType::Regular,389 )?;390391 let budget = budget::Value::new(NESTING_BUDGET);392393 let target_owner;394395 match new_owner {396 RmrkAccountIdOrCollectionNftTuple::AccountId(account_id) => {397 target_owner = T::CrossAccountId::from_sub(account_id);398 },399 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(target_collection_id, target_nft_id) => {400 let target_collection_id = Self::unique_collection_id(target_collection_id)?;401402 target_owner = T::CrossTokenAddressMapping::token_to_address(403 target_collection_id,404 target_nft_id.into(),405 );406407 let spender = <PalletStructure<T>>::get_indirect_owner(408 target_collection_id,409 target_nft_id.into(),410 Some((collection_id, nft_id)),411 &budget,412 )?;413414 let is_approval_required = cross_sender != spender;415416 if is_approval_required {417 <PalletNft<T>>::set_allowance(418 &collection,419 &cross_sender,420 nft_id,421 Some(&spender),422 &budget423 ).map_err(Self::map_common_err_to_proxy)?;424425 return Ok(());426 }427 }428 }429430 <PalletNft<T>>::transfer_from(431 &collection,432 &cross_sender,433 &from,434 &target_owner,435 nft_id,436 &budget437 ).map_err(Self::map_common_err_to_proxy)?;438439 Ok(())440 }441442 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]443 #[transactional]444 pub fn set_property(445 origin: OriginFor<T>,446 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,447 maybe_nft_id: Option<RmrkNftId>,448 key: RmrkKeyString,449 value: RmrkValueString,450 ) -> DispatchResult {451 let sender = ensure_signed(origin)?;452 let sender = T::CrossAccountId::from_sub(sender);453454 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;455 let budget = budget::Value::new(NESTING_BUDGET);456457 match maybe_nft_id {458 Some(nft_id) => {459 let token_id: TokenId = nft_id.into();460461 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;462 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;463464 <PalletNft<T>>::set_scoped_token_property(465 collection_id,466 token_id,467 PropertyScope::Rmrk,468 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,469 )?;470 }471 None => {472 let collection = Self::get_typed_nft_collection(473 collection_id,474 misc::CollectionType::Regular,475 )?;476477 Self::check_collection_owner(&collection, &sender)?;478479 <PalletCommon<T>>::set_scoped_collection_property(480 collection_id,481 PropertyScope::Rmrk,482 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,483 )?;484 }485 }486487 Self::deposit_event(Event::PropertySet {488 collection_id: rmrk_collection_id,489 maybe_nft_id,490 key,491 value,492 });493494 Ok(())495 }496497 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]498 #[transactional]499 pub fn add_basic_resource(500 origin: OriginFor<T>,501 collection_id: RmrkCollectionId,502 nft_id: RmrkNftId,503 resource: RmrkBasicResource,504 ) -> DispatchResult {505 let sender = ensure_signed(origin.clone())?;506507 let resource_id = Self::resource_add(508 sender,509 Self::unique_collection_id(collection_id)?,510 nft_id.into(),511 [512 Self::rmrk_property(TokenType, &NftType::Resource)?,513 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,514 Self::rmrk_property(Src, &resource.src)?,515 Self::rmrk_property(Metadata, &resource.metadata)?,516 Self::rmrk_property(License, &resource.license)?,517 Self::rmrk_property(Thumb, &resource.thumb)?,518 ]519 .into_iter(),520 )?;521522 Self::deposit_event(Event::ResourceAdded {523 nft_id,524 resource_id,525 });526 Ok(())527 }528529 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]530 #[transactional]531 pub fn add_composable_resource(532 origin: OriginFor<T>,533 collection_id: RmrkCollectionId,534 nft_id: RmrkNftId,535 _resource_id: RmrkBoundedResource,536 resource: RmrkComposableResource,537 ) -> DispatchResult {538 let sender = ensure_signed(origin.clone())?;539540 let resource_id = Self::resource_add(541 sender,542 Self::unique_collection_id(collection_id)?,543 nft_id.into(),544 [545 Self::rmrk_property(TokenType, &NftType::Resource)?,546 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,547 Self::rmrk_property(Parts, &resource.parts)?,548 Self::rmrk_property(Base, &resource.base)?,549 Self::rmrk_property(Src, &resource.src)?,550 Self::rmrk_property(Metadata, &resource.metadata)?,551 Self::rmrk_property(License, &resource.license)?,552 Self::rmrk_property(Thumb, &resource.thumb)?,553 ]554 .into_iter(),555 )?;556557 Self::deposit_event(Event::ResourceAdded {558 nft_id,559 resource_id,560 });561 Ok(())562 }563564 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]565 #[transactional]566 pub fn add_slot_resource(567 origin: OriginFor<T>,568 collection_id: RmrkCollectionId,569 nft_id: RmrkNftId,570 resource: RmrkSlotResource,571 ) -> DispatchResult {572 let sender = ensure_signed(origin.clone())?;573574 let resource_id = Self::resource_add(575 sender,576 Self::unique_collection_id(collection_id)?,577 nft_id.into(),578 [579 Self::rmrk_property(TokenType, &NftType::Resource)?,580 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,581 Self::rmrk_property(Base, &resource.base)?,582 Self::rmrk_property(Src, &resource.src)?,583 Self::rmrk_property(Metadata, &resource.metadata)?,584 Self::rmrk_property(Slot, &resource.slot)?,585 Self::rmrk_property(License, &resource.license)?,586 Self::rmrk_property(Thumb, &resource.thumb)?,587 ]588 .into_iter(),589 )?;590591 Self::deposit_event(Event::ResourceAdded {592 nft_id,593 resource_id,594 });595 Ok(())596 }597598 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]599 #[transactional]600 pub fn remove_resource(601 origin: OriginFor<T>,602 collection_id: RmrkCollectionId,603 nft_id: RmrkNftId,604 resource_id: RmrkResourceId,605 ) -> DispatchResult {606 let sender = ensure_signed(origin.clone())?;607608 Self::resource_remove(609 sender,610 Self::unique_collection_id(collection_id)?,611 nft_id.into(),612 resource_id.into(),613 )?;614615 Self::deposit_event(Event::ResourceRemoval {616 nft_id,617 resource_id,618 });619 Ok(())620 }621 }622}623624impl<T: Config> Pallet<T> {625 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {626 let key = rmrk_key.to_key::<T>()?;627628 let scoped_key = PropertyScope::Rmrk629 .apply(key)630 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;631632 Ok(scoped_key)633 }634635 // todo think about renaming these636 pub fn rmrk_property<E: Encode>(637 rmrk_key: RmrkProperty,638 value: &E,639 ) -> Result<Property, DispatchError> {640 let key = rmrk_key.to_key::<T>()?;641642 let value = value643 .encode()644 .try_into()645 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;646647 let property = Property { key, value };648649 Ok(property)650 }651652 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {653 vec.decode()654 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())655 }656657 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>658 where659 BoundedVec<u8, S>: TryFrom<Vec<u8>>,660 {661 vec.rebind()662 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())663 }664665 fn init_collection(666 sender: T::CrossAccountId,667 data: CreateCollectionData<T::AccountId>,668 properties: impl Iterator<Item = Property>,669 ) -> Result<CollectionId, DispatchError> {670 let collection_id = <PalletNft<T>>::init_collection(sender, data);671672 if let Err(DispatchError::Arithmetic(_)) = &collection_id {673 return Err(<Error<T>>::NoAvailableCollectionId.into());674 }675676 <PalletCommon<T>>::set_scoped_collection_properties(677 collection_id?,678 PropertyScope::Rmrk,679 properties,680 )?;681682 collection_id683 }684685 pub fn create_nft(686 sender: &T::CrossAccountId,687 owner: &T::CrossAccountId,688 collection: &NonfungibleHandle<T>,689 properties: impl Iterator<Item = Property>,690 ) -> Result<TokenId, DispatchError> {691 let data = CreateNftExData {692 properties: BoundedVec::default(),693 owner: owner.clone(),694 };695696 let budget = budget::Value::new(NESTING_BUDGET);697698 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;699700 let nft_id = <PalletNft<T>>::current_token_id(collection.id);701702 <PalletNft<T>>::set_scoped_token_properties(703 collection.id,704 nft_id,705 PropertyScope::Rmrk,706 properties,707 )?;708709 Ok(nft_id)710 }711712 fn destroy_nft(713 sender: T::CrossAccountId,714 collection_id: CollectionId,715 collection_type: misc::CollectionType,716 token_id: TokenId,717 ) -> DispatchResult {718 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;719720 <PalletNft<T>>::burn(&collection, &sender, token_id)721 .map_err(Self::map_common_err_to_proxy)?;722723 Ok(())724 }725726 fn resource_add(727 sender: T::AccountId,728 collection_id: CollectionId,729 token_id: TokenId,730 resource_properties: impl Iterator<Item = Property>,731 ) -> Result<RmrkResourceId, DispatchError> {732 let collection =733 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;734 ensure!(collection.owner == sender, Error::<T>::NoPermission);735736 // Check NFT lock status // todo depends on market, maybe later737 //ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);738739 let sender = T::CrossAccountId::from_sub(sender);740 let budget = budget::Value::new(NESTING_BUDGET);741 let pending = Self::ensure_nft_owner(collection_id, token_id, &sender, &budget).is_err();742743 let resource_collection_id: CollectionId =744 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;745 let resource_collection =746 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;747748 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them749750 let resource_id = Self::create_nft(751 &sender, // todo owner of the nft?752 &sender,753 &resource_collection,754 resource_properties.chain(755 [756 Self::rmrk_property(PendingResourceAccept, &pending)?,757 Self::rmrk_property(PendingResourceRemoval, &false)?,758 ]759 .into_iter(),760 ),761 )762 .map_err(|err| match err {763 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),764 err => Self::map_common_err_to_proxy(err),765 })?;766767 Ok(resource_id.0)768 }769770 fn resource_remove(771 sender: T::AccountId,772 collection_id: CollectionId,773 nft_id: TokenId,774 resource_id: TokenId,775 ) -> DispatchResult {776 let collection =777 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;778 ensure!(collection.owner == sender, Error::<T>::NoPermission);779780 let resource_collection_id: CollectionId =781 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;782 let resource_collection =783 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;784 ensure!(785 <PalletNft<T>>::token_exists(&resource_collection, resource_id),786 Error::<T>::ResourceDoesntExist787 );788789 let budget = up_data_structs::budget::Value::new(10);790 let topmost_owner =791 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;792793 let sender = T::CrossAccountId::from_sub(sender);794 if topmost_owner == sender {795 <PalletNft<T>>::burn(&collection, &sender, nft_id)796 .map_err(Self::map_common_err_to_proxy)?;797 } else {798 <PalletNft<T>>::set_scoped_token_property(799 collection_id,800 nft_id,801 PropertyScope::Rmrk,802 Self::rmrk_property(PendingResourceRemoval, &true)?,803 )?;804 }805806 Ok(())807 }808809 fn change_collection_owner(810 collection_id: CollectionId,811 collection_type: misc::CollectionType,812 sender: T::AccountId,813 new_owner: T::AccountId,814 ) -> DispatchResult {815 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;816 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;817818 let mut collection = collection.into_inner();819820 collection.owner = new_owner;821 collection.save()822 }823824 fn check_collection_owner(825 collection: &NonfungibleHandle<T>,826 account: &T::CrossAccountId,827 ) -> DispatchResult {828 collection829 .check_is_owner(account)830 .map_err(Self::map_common_err_to_proxy)831 }832833 pub fn last_collection_idx() -> RmrkCollectionId {834 <CollectionIndex<T>>::get()835 }836837 pub fn unique_collection_id(838 rmrk_collection_id: RmrkCollectionId,839 ) -> Result<CollectionId, DispatchError> {840 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)841 .map_err(|_| <Error<T>>::CollectionUnknown.into())842 }843844 pub fn rmrk_collection_id(845 unique_collection_id: CollectionId846 ) -> Result<RmrkCollectionId, DispatchError> {847 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)848 .map_err(|_| <Error<T>>::CollectionUnknown.into())849 }850851 pub fn get_nft_collection(852 collection_id: CollectionId,853 ) -> Result<NonfungibleHandle<T>, DispatchError> {854 let collection = <CollectionHandle<T>>::try_get(collection_id)855 .map_err(|_| <Error<T>>::CollectionUnknown)?;856857 match collection.mode {858 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),859 _ => Err(<Error<T>>::CollectionUnknown.into()),860 }861 }862863 pub fn collection_exists(collection_id: CollectionId) -> bool {864 <CollectionHandle<T>>::try_get(collection_id).is_ok()865 }866867 pub fn get_collection_property(868 collection_id: CollectionId,869 key: RmrkProperty,870 ) -> Result<PropertyValue, DispatchError> {871 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)872 .get(&Self::rmrk_property_key(key)?)873 .ok_or(<Error<T>>::CollectionUnknown)?874 .clone();875876 Ok(collection_property)877 }878879 pub fn get_collection_property_decoded<V: Decode>(880 collection_id: CollectionId,881 key: RmrkProperty,882 ) -> Result<V, DispatchError> {883 Self::decode_property(Self::get_collection_property(collection_id, key)?)884 }885886 pub fn get_collection_type(887 collection_id: CollectionId,888 ) -> Result<misc::CollectionType, DispatchError> {889 Self::get_collection_property_decoded(collection_id, CollectionType)890 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())891 }892893 pub fn ensure_collection_type(894 collection_id: CollectionId,895 collection_type: misc::CollectionType,896 ) -> DispatchResult {897 let actual_type = Self::get_collection_type(collection_id)?;898 ensure!(899 actual_type == collection_type,900 <CommonError<T>>::NoPermission901 );902903 Ok(())904 }905906 pub fn get_typed_nft_collection(907 collection_id: CollectionId,908 collection_type: misc::CollectionType,909 ) -> Result<NonfungibleHandle<T>, DispatchError> {910 Self::ensure_collection_type(collection_id, collection_type)?;911912 Self::get_nft_collection(collection_id)913 }914915 pub fn get_nft_property(916 collection_id: CollectionId,917 nft_id: TokenId,918 key: RmrkProperty,919 ) -> Result<PropertyValue, DispatchError> {920 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))921 .get(&Self::rmrk_property_key(key)?)922 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?923 .clone();924925 Ok(nft_property)926 }927928 pub fn get_nft_property_decoded<V: Decode>(929 collection_id: CollectionId,930 nft_id: TokenId,931 key: RmrkProperty,932 ) -> Result<V, DispatchError> {933 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)934 }935936 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {937 <TokenData<T>>::contains_key((collection_id, nft_id))938 }939940 pub fn get_nft_type(941 collection_id: CollectionId,942 token_id: TokenId,943 ) -> Result<NftType, DispatchError> {944 Self::get_nft_property_decoded(collection_id, token_id, TokenType)945 .map_err(|_| <Error<T>>::NftTypeEncodeError.into())946 }947948 pub fn ensure_nft_type(949 collection_id: CollectionId,950 token_id: TokenId,951 nft_type: NftType,952 ) -> DispatchResult {953 let actual_type = Self::get_nft_type(collection_id, token_id)?;954 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);955956 Ok(())957 }958959 pub fn ensure_nft_owner(960 collection_id: CollectionId,961 token_id: TokenId,962 possible_owner: &T::CrossAccountId,963 nesting_budget: &dyn budget::Budget964 ) -> DispatchResult {965 let is_owned = <PalletStructure<T>>::check_indirectly_owned(966 possible_owner.clone(),967 collection_id,968 token_id,969 None,970 nesting_budget,971 )?;972973 ensure!(974 is_owned,975 <Error<T>>::NoPermission976 );977978 Ok(())979 }980981 pub fn filter_user_properties<Key, Value, R, Mapper>(982 collection_id: CollectionId,983 token_id: Option<TokenId>,984 filter_keys: Option<Vec<RmrkPropertyKey>>,985 mapper: Mapper,986 ) -> Result<Vec<R>, DispatchError>987 where988 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,989 Value: Decode + Default,990 Mapper: Fn(Key, Value) -> R,991 {992 filter_keys993 .map(|keys| {994 let properties = keys995 .into_iter()996 .filter_map(|key| {997 let key: Key = key.try_into().ok()?;998999 let value = match token_id {1000 Some(token_id) => Self::get_nft_property_decoded(1001 collection_id,1002 token_id,1003 UserProperty(key.as_ref()),1004 ),1005 None => Self::get_collection_property_decoded(1006 collection_id,1007 UserProperty(key.as_ref()),1008 ),1009 }1010 .ok()?;10111012 Some(mapper(key, value))1013 })1014 .collect();10151016 Ok(properties)1017 })1018 .unwrap_or_else(|| {1019 let properties =1020 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();10211022 Ok(properties)1023 })1024 }10251026 pub fn iterate_user_properties<Key, Value, R, Mapper>(1027 collection_id: CollectionId,1028 token_id: Option<TokenId>,1029 mapper: Mapper,1030 ) -> Result<impl Iterator<Item = R>, DispatchError>1031 where1032 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1033 Value: Decode + Default,1034 Mapper: Fn(Key, Value) -> R,1035 {1036 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;10371038 let properties = match token_id {1039 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1040 None => <PalletCommon<T>>::collection_properties(collection_id),1041 };10421043 let properties = properties.into_iter().filter_map(move |(key, value)| {1044 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;10451046 let key: Key = key.to_vec().try_into().ok()?;1047 let value: Value = value.decode().ok()?;10481049 Some(mapper(key, value))1050 });10511052 Ok(properties)1053 }10541055 fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {1056 map_common_err_to_proxy! {1057 match err {1058 NoPermission => NoPermission,1059 CollectionTokenLimitExceeded => CollectionFullOrLocked,1060 PublicMintingNotAllowed => NoPermission,1061 TokenNotFound => NoAvailableNftId,1062 ApprovedValueTooLow => NoPermission1063 }1064 }1065 }1066}runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -147,12 +147,7 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
- };
-
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
};
@@ -173,11 +168,7 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
- };
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
};
@@ -213,11 +204,11 @@
use pallet_common::CommonCollectionOperations;
let cross_account_id = CrossAccountId::from_sub(account_id);
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
+
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
};
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
Ok(
collection.account_tokens(cross_account_id)
@@ -387,11 +378,7 @@
RmrkProperty, misc::{CollectionType},
};
- let collection_id = match RmrkCore::unique_collection_id(base_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
- };
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
Ok(c) => c,
Err(_) => return Ok(None),
};
@@ -407,12 +394,11 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
use pallet_common::CommonCollectionOperations;
- let collection_id = match RmrkCore::unique_collection_id(base_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
};
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
-
+
let parts = collection.collection_tokens()
.into_iter()
.filter_map(|token_id| {
@@ -442,14 +428,10 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = match RmrkCore::unique_collection_id(base_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
};
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
- return Ok(Vec::new());
- }
-
let theme_names = collection.collection_tokens()
.iter()
@@ -475,13 +457,10 @@
};
use pallet_common::CommonCollectionOperations;
- let collection_id = match RmrkCore::unique_collection_id(base_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
+ let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
+ Ok(c) => c,
+ Err(_) => return Ok(None),
};
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
- return Ok(None);
- }
let theme_info = collection.collection_tokens()
.into_iter()