difftreelog
refactor move RMRK rpc impl to proxy pallets
in: master
6 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, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod misc;37pub mod property;38pub mod weights;3940pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4142use weights::WeightInfo;43use misc::*;44pub use property::*;4546use RmrkProperty::*;4748pub const NESTING_BUDGET: u32 = 5;4950#[frame_support::pallet]51pub mod pallet {52 use super::*;53 use pallet_evm::account;5455 #[pallet::config]56 pub trait Config:57 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config58 {59 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;60 type WeightInfo: WeightInfo;61 }6263 #[pallet::storage]64 #[pallet::getter(fn collection_index)]65 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;6667 #[pallet::storage]68 pub type UniqueCollectionId<T: Config> =69 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;7071 #[pallet::pallet]72 #[pallet::generate_store(pub(super) trait Store)]73 pub struct Pallet<T>(_);7475 #[pallet::event]76 #[pallet::generate_deposit(pub(super) fn deposit_event)]77 pub enum Event<T: Config> {78 CollectionCreated {79 issuer: T::AccountId,80 collection_id: RmrkCollectionId,81 },82 CollectionDestroyed {83 issuer: T::AccountId,84 collection_id: RmrkCollectionId,85 },86 IssuerChanged {87 old_issuer: T::AccountId,88 new_issuer: T::AccountId,89 collection_id: RmrkCollectionId,90 },91 CollectionLocked {92 issuer: T::AccountId,93 collection_id: RmrkCollectionId,94 },95 NftMinted {96 owner: T::AccountId,97 collection_id: RmrkCollectionId,98 nft_id: RmrkNftId,99 },100 NFTBurned {101 owner: T::AccountId,102 nft_id: RmrkNftId,103 },104 NFTSent {105 sender: T::AccountId,106 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,107 collection_id: RmrkCollectionId,108 nft_id: RmrkNftId,109 approval_required: bool,110 },111 NFTAccepted {112 sender: T::AccountId,113 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,114 collection_id: RmrkCollectionId,115 nft_id: RmrkNftId,116 },117 NFTRejected {118 sender: T::AccountId,119 collection_id: RmrkCollectionId,120 nft_id: RmrkNftId,121 },122 PropertySet {123 collection_id: RmrkCollectionId,124 maybe_nft_id: Option<RmrkNftId>,125 key: RmrkKeyString,126 value: RmrkValueString,127 },128 ResourceAdded {129 nft_id: RmrkNftId,130 resource_id: RmrkResourceId,131 },132 ResourceRemoval {133 nft_id: RmrkNftId,134 resource_id: RmrkResourceId,135 },136 ResourceAccepted {137 nft_id: RmrkNftId,138 resource_id: RmrkResourceId,139 },140 ResourceRemovalAccepted {141 nft_id: RmrkNftId,142 resource_id: RmrkResourceId,143 },144 PrioritySet {145 collection_id: RmrkCollectionId,146 nft_id: RmrkNftId,147 },148 }149150 #[pallet::error]151 pub enum Error<T> {152 /* Unique-specific events */153 CorruptedCollectionType,154 NftTypeEncodeError,155 RmrkPropertyKeyIsTooLong,156 RmrkPropertyValueIsTooLong,157 RmrkPropertyIsNotFound,158 UnableToDecodeRmrkData,159160 /* RMRK compatible events */161 CollectionNotEmpty,162 NoAvailableCollectionId,163 NoAvailableNftId,164 CollectionUnknown,165 NoPermission,166 NonTransferable,167 CollectionFullOrLocked,168 ResourceDoesntExist,169 CannotSendToDescendentOrSelf,170 CannotAcceptNonOwnedNft,171 CannotRejectNonOwnedNft,172 CannotRejectNonPendingNft,173 ResourceNotPending,174 NoAvailableResourceId,175 }176177 #[pallet::call]178 impl<T: Config> Pallet<T> {179 /// Create a collection180 #[transactional]181 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]182 pub fn create_collection(183 origin: OriginFor<T>,184 metadata: RmrkString,185 max: Option<u32>,186 symbol: RmrkCollectionSymbol,187 ) -> DispatchResult {188 let sender = ensure_signed(origin)?;189190 let limits = CollectionLimits {191 owner_can_transfer: Some(false),192 token_limit: max,193 ..Default::default()194 };195196 let data = CreateCollectionData {197 limits: Some(limits),198 token_prefix: symbol199 .into_inner()200 .try_into()201 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,202 permissions: Some(CollectionPermissions {203 nesting: Some(NestingPermissions {204 token_owner: true,205 collection_admin: false,206 restricted: None,207208 permissive: false,209 }),210 ..Default::default()211 }),212 ..Default::default()213 };214215 let unique_collection_id = Self::init_collection(216 T::CrossAccountId::from_sub(sender.clone()),217 data,218 [219 Self::rmrk_property(Metadata, &metadata)?,220 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,221 ]222 .into_iter(),223 )?;224 let rmrk_collection_id = <CollectionIndex<T>>::get();225226 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);227228 <PalletCommon<T>>::set_scoped_collection_property(229 unique_collection_id,230 PropertyScope::Rmrk,231 Self::rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,232 )?;233234 <CollectionIndex<T>>::mutate(|n| *n += 1);235236 Self::deposit_event(Event::CollectionCreated {237 issuer: sender,238 collection_id: rmrk_collection_id,239 });240241 Ok(())242 }243244 /// destroy collection245 #[transactional]246 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]247 pub fn destroy_collection(248 origin: OriginFor<T>,249 collection_id: RmrkCollectionId,250 ) -> DispatchResult {251 let sender = ensure_signed(origin)?;252 let cross_sender = T::CrossAccountId::from_sub(sender.clone());253254 let collection = Self::get_typed_nft_collection(255 Self::unique_collection_id(collection_id)?,256 misc::CollectionType::Regular,257 )?;258 collection.check_is_external()?;259260 <PalletNft<T>>::destroy_collection(collection, &cross_sender)261 .map_err(Self::map_unique_err_to_proxy)?;262263 Self::deposit_event(Event::CollectionDestroyed {264 issuer: sender,265 collection_id,266 });267268 Ok(())269 }270271 /// Change the issuer of a collection272 ///273 /// Parameters:274 /// - `origin`: sender of the transaction275 /// - `collection_id`: collection id of the nft to change issuer of276 /// - `new_issuer`: Collection's new issuer277 #[transactional]278 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]279 pub fn change_collection_issuer(280 origin: OriginFor<T>,281 collection_id: RmrkCollectionId,282 new_issuer: <T::Lookup as StaticLookup>::Source,283 ) -> DispatchResult {284 let sender = ensure_signed(origin)?;285286 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;287 collection.check_is_external()?;288289 let new_issuer = T::Lookup::lookup(new_issuer)?;290291 Self::change_collection_owner(292 Self::unique_collection_id(collection_id)?,293 misc::CollectionType::Regular,294 sender.clone(),295 new_issuer.clone(),296 )?;297298 Self::deposit_event(Event::IssuerChanged {299 old_issuer: sender,300 new_issuer,301 collection_id,302 });303304 Ok(())305 }306307 /// lock collection308 #[transactional]309 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]310 pub fn lock_collection(311 origin: OriginFor<T>,312 collection_id: RmrkCollectionId,313 ) -> DispatchResult {314 let sender = ensure_signed(origin)?;315 let cross_sender = T::CrossAccountId::from_sub(sender.clone());316317 let collection = Self::get_typed_nft_collection(318 Self::unique_collection_id(collection_id)?,319 misc::CollectionType::Regular,320 )?;321 collection.check_is_external()?;322323 Self::check_collection_owner(&collection, &cross_sender)?;324325 let token_count = collection.total_supply();326327 let mut collection = collection.into_inner();328 collection.limits.token_limit = Some(token_count);329 collection.save()?;330331 Self::deposit_event(Event::CollectionLocked {332 issuer: sender,333 collection_id,334 });335336 Ok(())337 }338339 /// Mints an NFT in the specified collection340 /// Sets metadata and the royalty attribute341 ///342 /// Parameters:343 /// - `collection_id`: The class of the asset to be minted.344 /// - `nft_id`: The nft value of the asset to be minted.345 /// - `recipient`: Receiver of the royalty346 /// - `royalty`: Permillage reward from each trade for the Recipient347 /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash348 /// - `transferable`: Ability to transfer this NFT349 #[transactional]350 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]351 pub fn mint_nft(352 origin: OriginFor<T>,353 owner: Option<T::AccountId>,354 collection_id: RmrkCollectionId,355 recipient: Option<T::AccountId>,356 royalty_amount: Option<Permill>,357 metadata: RmrkString,358 transferable: bool,359 resources: Option<BoundedVec<RmrkResourceTypes, MaxResourcesOnMint>>,360 ) -> DispatchResult {361 let sender = ensure_signed(origin)?;362 let cross_sender = T::CrossAccountId::from_sub(sender.clone());363364 let owner = owner.unwrap_or(sender.clone());365 let cross_owner = T::CrossAccountId::from_sub(owner.clone());366367 let collection = Self::get_typed_nft_collection(368 Self::unique_collection_id(collection_id)?,369 misc::CollectionType::Regular,370 )?;371 collection.check_is_external()?;372373 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {374 recipient: recipient.unwrap_or_else(|| owner.clone()),375 amount,376 });377378 let nft_id = Self::create_nft(379 &cross_sender,380 &cross_owner,381 &collection,382 [383 Self::rmrk_property(TokenType, &NftType::Regular)?,384 Self::rmrk_property(Transferable, &transferable)?,385 Self::rmrk_property(PendingNftAccept, &false)?,386 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,387 Self::rmrk_property(Metadata, &metadata)?,388 Self::rmrk_property(Equipped, &false)?,389 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,390 Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,391 ]392 .into_iter(),393 )394 .map_err(|err| match err {395 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),396 err => Self::map_unique_err_to_proxy(err),397 })?;398399 if let Some(resources) = resources {400 for resource in resources {401 Self::resource_add(sender.clone(), collection.id, nft_id, resource)?;402 }403 }404405 Self::deposit_event(Event::NftMinted {406 owner,407 collection_id,408 nft_id: nft_id.0,409 });410411 Ok(())412 }413414 /// burn nft415 #[transactional]416 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]417 pub fn burn_nft(418 origin: OriginFor<T>,419 collection_id: RmrkCollectionId,420 nft_id: RmrkNftId,421 max_burns: u32,422 ) -> DispatchResult {423 let sender = ensure_signed(origin)?;424 let cross_sender = T::CrossAccountId::from_sub(sender.clone());425426 let collection = Self::get_typed_nft_collection(427 Self::unique_collection_id(collection_id)?,428 misc::CollectionType::Regular,429 )?;430 collection.check_is_external()?;431432 Self::destroy_nft(433 cross_sender,434 Self::unique_collection_id(collection_id)?,435 nft_id.into(),436 max_burns,437 <Error<T>>::NoPermission,438 )439 .map_err(|err| Self::map_unique_err_to_proxy(err.error))?;440441 Self::deposit_event(Event::NFTBurned {442 owner: sender,443 nft_id,444 });445446 Ok(())447 }448449 /// Transfers a NFT from an Account or NFT A to another Account or NFT B450 ///451 /// Parameters:452 /// - `origin`: sender of the transaction453 /// - `rmrk_collection_id`: collection id of the nft to be transferred454 /// - `rmrk_nft_id`: nft id of the nft to be transferred455 /// - `new_owner`: new owner of the nft which can be either an account or a NFT456 #[transactional]457 #[pallet::weight(<SelfWeightOf<T>>::send())]458 pub fn send(459 origin: OriginFor<T>,460 rmrk_collection_id: RmrkCollectionId,461 rmrk_nft_id: RmrkNftId,462 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,463 ) -> DispatchResult {464 let sender = ensure_signed(origin.clone())?;465 let cross_sender = T::CrossAccountId::from_sub(sender.clone());466467 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;468 let nft_id = rmrk_nft_id.into();469470 let collection =471 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;472 collection.check_is_external()?;473474 let token_data =475 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;476477 let from = token_data.owner;478479 ensure!(480 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,481 <Error<T>>::NonTransferable482 );483484 ensure!(485 !Self::get_nft_property_decoded(486 collection_id,487 nft_id,488 RmrkProperty::PendingNftAccept489 )?,490 <Error<T>>::NoPermission491 );492493 let target_owner;494 let approval_required;495496 match new_owner {497 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {498 target_owner = T::CrossAccountId::from_sub(account_id.clone());499 approval_required = false;500 }501 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(502 target_collection_id,503 target_nft_id,504 ) => {505 let target_collection_id = Self::unique_collection_id(target_collection_id)?;506507 let target_nft_budget = budget::Value::new(NESTING_BUDGET);508509 let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(510 target_collection_id,511 target_nft_id.into(),512 Some((collection_id, nft_id)),513 &target_nft_budget,514 )515 .map_err(Self::map_unique_err_to_proxy)?;516517 approval_required = cross_sender != target_nft_owner;518519 if approval_required {520 target_owner = target_nft_owner;521522 <PalletNft<T>>::set_scoped_token_property(523 collection.id,524 nft_id,525 PropertyScope::Rmrk,526 Self::rmrk_property(PendingNftAccept, &approval_required)?,527 )?;528 } else {529 target_owner = T::CrossTokenAddressMapping::token_to_address(530 target_collection_id,531 target_nft_id.into(),532 );533 }534 }535 }536537 let src_nft_budget = budget::Value::new(NESTING_BUDGET);538539 <PalletNft<T>>::transfer_from(540 &collection,541 &cross_sender,542 &from,543 &target_owner,544 nft_id,545 &src_nft_budget,546 )547 .map_err(Self::map_unique_err_to_proxy)?;548549 Self::deposit_event(Event::NFTSent {550 sender,551 recipient: new_owner,552 collection_id: rmrk_collection_id,553 nft_id: rmrk_nft_id,554 approval_required,555 });556557 Ok(())558 }559560 /// Accepts an NFT sent from another account to self or owned NFT561 ///562 /// Parameters:563 /// - `origin`: sender of the transaction564 /// - `rmrk_collection_id`: collection id of the nft to be accepted565 /// - `rmrk_nft_id`: nft id of the nft to be accepted566 /// - `new_owner`: either origin's account ID or origin-owned NFT, whichever the NFT was567 /// sent to568 #[transactional]569 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]570 pub fn accept_nft(571 origin: OriginFor<T>,572 rmrk_collection_id: RmrkCollectionId,573 rmrk_nft_id: RmrkNftId,574 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,575 ) -> DispatchResult {576 let sender = ensure_signed(origin.clone())?;577 let cross_sender = T::CrossAccountId::from_sub(sender.clone());578579 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;580 let nft_id = rmrk_nft_id.into();581582 let collection =583 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;584 collection.check_is_external()?;585586 let new_cross_owner = match new_owner {587 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {588 T::CrossAccountId::from_sub(account_id.clone())589 }590 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(591 target_collection_id,592 target_nft_id,593 ) => {594 let target_collection_id = Self::unique_collection_id(target_collection_id)?;595596 T::CrossTokenAddressMapping::token_to_address(597 target_collection_id,598 TokenId(target_nft_id),599 )600 }601 };602603 let budget = budget::Value::new(NESTING_BUDGET);604605 <PalletNft<T>>::transfer(606 &collection,607 &cross_sender,608 &new_cross_owner,609 nft_id,610 &budget,611 )612 .map_err(|err| {613 if err == <CommonError<T>>::UserIsNotAllowedToNest.into() {614 <Error<T>>::CannotAcceptNonOwnedNft.into()615 } else {616 Self::map_unique_err_to_proxy(err)617 }618 })?;619620 <PalletNft<T>>::set_scoped_token_property(621 collection.id,622 nft_id,623 PropertyScope::Rmrk,624 Self::rmrk_property(PendingNftAccept, &false)?,625 )?;626627 Self::deposit_event(Event::NFTAccepted {628 sender,629 recipient: new_owner,630 collection_id: rmrk_collection_id,631 nft_id: rmrk_nft_id,632 });633634 Ok(())635 }636637 /// Rejects an NFT sent from another account to self or owned NFT638 ///639 /// Parameters:640 /// - `origin`: sender of the transaction641 /// - `rmrk_collection_id`: collection id of the nft to be accepted642 /// - `rmrk_nft_id`: nft id of the nft to be accepted643 #[transactional]644 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]645 pub fn reject_nft(646 origin: OriginFor<T>,647 rmrk_collection_id: RmrkCollectionId,648 rmrk_nft_id: RmrkNftId,649 ) -> DispatchResult {650 let sender = ensure_signed(origin)?;651 let cross_sender = T::CrossAccountId::from_sub(sender.clone());652653 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;654 let nft_id = rmrk_nft_id.into();655656 let collection =657 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;658 collection.check_is_external()?;659660 ensure!(661 <TokenData<T>>::get((collection_id, nft_id)).is_some(),662 <Error<T>>::NoAvailableNftId663 );664665 ensure!(666 Self::get_nft_property_decoded(667 collection_id,668 nft_id,669 RmrkProperty::PendingNftAccept670 )?,671 <Error<T>>::CannotRejectNonPendingNft672 );673674 Self::destroy_nft(675 cross_sender,676 collection_id,677 nft_id,678 NESTING_BUDGET,679 <Error<T>>::CannotRejectNonOwnedNft,680 )681 .map_err(|err| Self::map_unique_err_to_proxy(err.error))?;682683 Self::deposit_event(Event::NFTRejected {684 sender,685 collection_id: rmrk_collection_id,686 nft_id: rmrk_nft_id,687 });688689 Ok(())690 }691692 /// accept the addition of a new resource to an existing NFT693 #[transactional]694 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]695 pub fn accept_resource(696 origin: OriginFor<T>,697 rmrk_collection_id: RmrkCollectionId,698 rmrk_nft_id: RmrkNftId,699 resource_id: RmrkResourceId,700 ) -> DispatchResult {701 let sender = ensure_signed(origin)?;702 let cross_sender = T::CrossAccountId::from_sub(sender);703704 let collection_id = Self::unique_collection_id(rmrk_collection_id)705 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;706 let collection =707 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;708 collection.check_is_external()?;709710 let nft_id = rmrk_nft_id.into();711712 let budget = budget::Value::new(NESTING_BUDGET);713714 let nft_owner =715 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)716 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;717718 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {719 ensure!(res.pending, <Error<T>>::ResourceNotPending);720 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);721722 res.pending = false;723724 Ok(())725 })?;726727 Self::deposit_event(Event::<T>::ResourceAccepted {728 nft_id: rmrk_nft_id,729 resource_id,730 });731732 Ok(())733 }734735 /// accept the removal of a resource of an existing NFT736 #[transactional]737 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]738 pub fn accept_resource_removal(739 origin: OriginFor<T>,740 rmrk_collection_id: RmrkCollectionId,741 rmrk_nft_id: RmrkNftId,742 resource_id: RmrkResourceId,743 ) -> DispatchResult {744 let sender = ensure_signed(origin)?;745 let cross_sender = T::CrossAccountId::from_sub(sender);746747 let collection_id = Self::unique_collection_id(rmrk_collection_id)748 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;749 let collection =750 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;751 collection.check_is_external()?;752753 let nft_id = rmrk_nft_id.into();754755 let budget = budget::Value::new(NESTING_BUDGET);756757 let nft_owner =758 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)759 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;760761 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);762763 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;764765 let resource_info = <PalletNft<T>>::token_aux_property((766 collection_id,767 nft_id,768 PropertyScope::Rmrk,769 resource_id_key.clone(),770 ))771 .ok_or(<Error<T>>::ResourceDoesntExist)?;772773 let resource_info: RmrkResourceInfo = Self::decode_property(&resource_info)?;774775 ensure!(776 resource_info.pending_removal,777 <Error<T>>::ResourceNotPending778 );779780 <PalletNft<T>>::remove_token_aux_property(781 collection_id,782 nft_id,783 PropertyScope::Rmrk,784 resource_id_key,785 );786787 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {788 nft_id: rmrk_nft_id,789 resource_id,790 });791792 Ok(())793 }794795 /// set a custom value on an NFT796 #[transactional]797 #[pallet::weight(<SelfWeightOf<T>>::set_property())]798 pub fn set_property(799 origin: OriginFor<T>,800 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,801 maybe_nft_id: Option<RmrkNftId>,802 key: RmrkKeyString,803 value: RmrkValueString,804 ) -> DispatchResult {805 let sender = ensure_signed(origin)?;806 let sender = T::CrossAccountId::from_sub(sender);807808 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;809 let collection =810 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;811 collection.check_is_external()?;812813 let budget = budget::Value::new(NESTING_BUDGET);814815 match maybe_nft_id {816 Some(nft_id) => {817 let token_id: TokenId = nft_id.into();818819 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;820 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;821822 <PalletNft<T>>::set_scoped_token_property(823 collection_id,824 token_id,825 PropertyScope::Rmrk,826 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,827 )?;828 }829 None => {830 let collection = Self::get_typed_nft_collection(831 collection_id,832 misc::CollectionType::Regular,833 )?;834835 Self::check_collection_owner(&collection, &sender)?;836837 <PalletCommon<T>>::set_scoped_collection_property(838 collection_id,839 PropertyScope::Rmrk,840 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,841 )?;842 }843 }844845 Self::deposit_event(Event::PropertySet {846 collection_id: rmrk_collection_id,847 maybe_nft_id,848 key,849 value,850 });851852 Ok(())853 }854855 /// set a different order of resource priority856 #[transactional]857 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]858 pub fn set_priority(859 origin: OriginFor<T>,860 rmrk_collection_id: RmrkCollectionId,861 rmrk_nft_id: RmrkNftId,862 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,863 ) -> DispatchResult {864 let sender = ensure_signed(origin)?;865 let sender = T::CrossAccountId::from_sub(sender);866867 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;868 let nft_id = rmrk_nft_id.into();869870 let collection =871 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;872 collection.check_is_external()?;873874 let budget = budget::Value::new(NESTING_BUDGET);875876 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;877 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;878879 <PalletNft<T>>::set_scoped_token_property(880 collection_id,881 nft_id,882 PropertyScope::Rmrk,883 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,884 )?;885886 Self::deposit_event(Event::<T>::PrioritySet {887 collection_id: rmrk_collection_id,888 nft_id: rmrk_nft_id,889 });890891 Ok(())892 }893894 /// Create basic resource895 #[transactional]896 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]897 pub fn add_basic_resource(898 origin: OriginFor<T>,899 rmrk_collection_id: RmrkCollectionId,900 nft_id: RmrkNftId,901 resource: RmrkBasicResource,902 ) -> DispatchResult {903 let sender = ensure_signed(origin.clone())?;904905 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;906 let collection =907 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;908 collection.check_is_external()?;909910 let resource_id = Self::resource_add(911 sender,912 collection_id,913 nft_id.into(),914 RmrkResourceTypes::Basic(resource),915 )?;916917 Self::deposit_event(Event::ResourceAdded {918 nft_id,919 resource_id,920 });921 Ok(())922 }923924 /// Create composable resource925 #[transactional]926 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]927 pub fn add_composable_resource(928 origin: OriginFor<T>,929 rmrk_collection_id: RmrkCollectionId,930 nft_id: RmrkNftId,931 resource: RmrkComposableResource,932 ) -> DispatchResult {933 let sender = ensure_signed(origin.clone())?;934935 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;936 let collection =937 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;938 collection.check_is_external()?;939940 let resource_id = Self::resource_add(941 sender,942 collection_id,943 nft_id.into(),944 RmrkResourceTypes::Composable(resource),945 )?;946947 Self::deposit_event(Event::ResourceAdded {948 nft_id,949 resource_id,950 });951 Ok(())952 }953954 /// Create slot resource955 #[transactional]956 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]957 pub fn add_slot_resource(958 origin: OriginFor<T>,959 rmrk_collection_id: RmrkCollectionId,960 nft_id: RmrkNftId,961 resource: RmrkSlotResource,962 ) -> DispatchResult {963 let sender = ensure_signed(origin.clone())?;964965 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;966 let collection =967 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;968 collection.check_is_external()?;969970 let resource_id = Self::resource_add(971 sender,972 collection_id,973 nft_id.into(),974 RmrkResourceTypes::Slot(resource),975 )?;976977 Self::deposit_event(Event::ResourceAdded {978 nft_id,979 resource_id,980 });981 Ok(())982 }983984 /// remove resource985 #[transactional]986 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]987 pub fn remove_resource(988 origin: OriginFor<T>,989 rmrk_collection_id: RmrkCollectionId,990 nft_id: RmrkNftId,991 resource_id: RmrkResourceId,992 ) -> DispatchResult {993 let sender = ensure_signed(origin.clone())?;994995 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;996 let collection =997 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;998 collection.check_is_external()?;9991000 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id)?;10011002 Self::deposit_event(Event::ResourceRemoval {1003 nft_id,1004 resource_id,1005 });1006 Ok(())1007 }1008 }1009}10101011impl<T: Config> Pallet<T> {1012 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {1013 let key = rmrk_key.to_key::<T>()?;10141015 let scoped_key = PropertyScope::Rmrk1016 .apply(key)1017 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;10181019 Ok(scoped_key)1020 }10211022 // todo think about renaming these1023 pub fn rmrk_property<E: Encode>(1024 rmrk_key: RmrkProperty,1025 value: &E,1026 ) -> Result<Property, DispatchError> {1027 let key = rmrk_key.to_key::<T>()?;10281029 let value = Self::encode_property(value)?;10301031 let property = Property { key, value };10321033 Ok(property)1034 }10351036 pub fn encode_property<E: Encode, S: Get<u32>>(1037 value: &E,1038 ) -> Result<BoundedBytes<S>, DispatchError> {1039 let value = value1040 .encode()1041 .try_into()1042 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;10431044 Ok(value)1045 }10461047 pub fn decode_property<D: Decode, S: Get<u32>>(1048 vec: &BoundedBytes<S>,1049 ) -> Result<D, DispatchError> {1050 vec.decode()1051 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())1052 }10531054 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1055 where1056 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1057 {1058 vec.rebind()1059 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1060 }10611062 fn init_collection(1063 sender: T::CrossAccountId,1064 data: CreateCollectionData<T::AccountId>,1065 properties: impl Iterator<Item = Property>,1066 ) -> Result<CollectionId, DispatchError> {1067 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10681069 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1070 return Err(<Error<T>>::NoAvailableCollectionId.into());1071 }10721073 <PalletCommon<T>>::set_scoped_collection_properties(1074 collection_id?,1075 PropertyScope::Rmrk,1076 properties,1077 )?;10781079 collection_id1080 }10811082 pub fn create_nft(1083 sender: &T::CrossAccountId,1084 owner: &T::CrossAccountId,1085 collection: &NonfungibleHandle<T>,1086 properties: impl Iterator<Item = Property>,1087 ) -> Result<TokenId, DispatchError> {1088 let data = CreateNftExData {1089 properties: BoundedVec::default(),1090 owner: owner.clone(),1091 };10921093 let budget = budget::Value::new(NESTING_BUDGET);10941095 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10961097 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10981099 <PalletNft<T>>::set_scoped_token_properties(1100 collection.id,1101 nft_id,1102 PropertyScope::Rmrk,1103 properties,1104 )?;11051106 Ok(nft_id)1107 }11081109 fn destroy_nft(1110 sender: T::CrossAccountId,1111 collection_id: CollectionId,1112 token_id: TokenId,1113 max_burns: u32,1114 error_if_not_owned: Error<T>,1115 ) -> DispatchResultWithPostInfo {1116 let collection =1117 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;11181119 let token_data =1120 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;11211122 let from = token_data.owner;11231124 let owner_check_budget = budget::Value::new(NESTING_BUDGET);11251126 ensure!(1127 <PalletStructure<T>>::check_indirectly_owned(1128 sender.clone(),1129 collection_id,1130 token_id,1131 None,1132 &owner_check_budget1133 )?,1134 error_if_not_owned,1135 );11361137 let burns_budget = budget::Value::new(max_burns);1138 let breadth_budget = budget::Value::new(max_burns);11391140 <PalletNft<T>>::burn_recursively(1141 &collection,1142 &from,1143 token_id,1144 &burns_budget,1145 &breadth_budget,1146 )1147 }11481149 fn acquire_next_resource_id(1150 collection_id: CollectionId,1151 nft_id: TokenId,1152 ) -> Result<RmrkResourceId, DispatchError> {1153 let resource_id: RmrkResourceId =1154 Self::get_nft_property_decoded(collection_id, nft_id, NextResourceId)?;11551156 let next_id = resource_id1157 .checked_add(1)1158 .ok_or(<Error<T>>::NoAvailableResourceId)?;11591160 <PalletNft<T>>::set_scoped_token_property(1161 collection_id,1162 nft_id,1163 PropertyScope::Rmrk,1164 Self::rmrk_property(NextResourceId, &next_id)?,1165 )?;11661167 Ok(resource_id)1168 }11691170 fn resource_add(1171 sender: T::AccountId,1172 collection_id: CollectionId,1173 nft_id: TokenId,1174 resource: RmrkResourceTypes,1175 ) -> Result<RmrkResourceId, DispatchError> {1176 let collection =1177 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1178 ensure!(collection.owner == sender, Error::<T>::NoPermission);11791180 let sender = T::CrossAccountId::from_sub(sender);1181 let budget = budget::Value::new(NESTING_BUDGET);11821183 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1184 .map_err(Self::map_unique_err_to_proxy)?;11851186 let pending = sender != nft_owner;11871188 let id = Self::acquire_next_resource_id(collection_id, nft_id)?;11891190 let resource_info = RmrkResourceInfo {1191 id,1192 resource,1193 pending,1194 pending_removal: false,1195 };11961197 <PalletNft<T>>::try_mutate_token_aux_property(1198 collection_id,1199 nft_id,1200 PropertyScope::Rmrk,1201 Self::rmrk_property_key(ResourceId(id))?,1202 |value| -> DispatchResult {1203 *value = Some(Self::encode_property(&resource_info)?);12041205 Ok(())1206 },1207 )?;12081209 Ok(id)1210 }12111212 fn resource_remove(1213 sender: T::AccountId,1214 collection_id: CollectionId,1215 nft_id: TokenId,1216 resource_id: RmrkResourceId,1217 ) -> DispatchResult {1218 let collection =1219 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1220 ensure!(collection.owner == sender, Error::<T>::NoPermission);12211222 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1223 let scope = PropertyScope::Rmrk;12241225 ensure!(1226 <PalletNft<T>>::token_aux_property((1227 collection_id,1228 nft_id,1229 scope,1230 resource_id_key.clone()1231 ))1232 .is_some(),1233 <Error<T>>::ResourceDoesntExist1234 );12351236 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1237 let topmost_owner =1238 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;12391240 let sender = T::CrossAccountId::from_sub(sender);1241 if topmost_owner == sender {1242 <PalletNft<T>>::remove_token_aux_property(1243 collection_id,1244 nft_id,1245 PropertyScope::Rmrk,1246 Self::rmrk_property_key(ResourceId(resource_id))?,1247 );1248 } else {1249 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {1250 res.pending_removal = true;12511252 Ok(())1253 })?;1254 }12551256 Ok(())1257 }12581259 fn try_mutate_resource_info(1260 collection_id: CollectionId,1261 nft_id: TokenId,1262 resource_id: RmrkResourceId,1263 f: impl FnOnce(&mut RmrkResourceInfo) -> DispatchResult,1264 ) -> DispatchResult {1265 <PalletNft<T>>::try_mutate_token_aux_property(1266 collection_id,1267 nft_id,1268 PropertyScope::Rmrk,1269 Self::rmrk_property_key(ResourceId(resource_id))?,1270 |value| match value {1271 Some(value) => {1272 let mut resource_info: RmrkResourceInfo = Self::decode_property(value)?;12731274 f(&mut resource_info)?;12751276 *value = Self::encode_property(&resource_info)?;12771278 Ok(())1279 }1280 None => Err(<Error<T>>::ResourceDoesntExist.into()),1281 },1282 )1283 }12841285 fn change_collection_owner(1286 collection_id: CollectionId,1287 collection_type: misc::CollectionType,1288 sender: T::AccountId,1289 new_owner: T::AccountId,1290 ) -> DispatchResult {1291 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1292 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;12931294 let mut collection = collection.into_inner();12951296 collection.owner = new_owner;1297 collection.save()1298 }12991300 fn check_collection_owner(1301 collection: &NonfungibleHandle<T>,1302 account: &T::CrossAccountId,1303 ) -> DispatchResult {1304 collection1305 .check_is_owner(account)1306 .map_err(Self::map_unique_err_to_proxy)1307 }13081309 pub fn last_collection_idx() -> RmrkCollectionId {1310 <CollectionIndex<T>>::get()1311 }13121313 pub fn unique_collection_id(1314 rmrk_collection_id: RmrkCollectionId,1315 ) -> Result<CollectionId, DispatchError> {1316 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1317 .map_err(|_| <Error<T>>::CollectionUnknown.into())1318 }13191320 pub fn rmrk_collection_id(1321 unique_collection_id: CollectionId,1322 ) -> Result<RmrkCollectionId, DispatchError> {1323 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)1324 }13251326 pub fn get_nft_collection(1327 collection_id: CollectionId,1328 ) -> Result<NonfungibleHandle<T>, DispatchError> {1329 let collection = <CollectionHandle<T>>::try_get(collection_id)1330 .map_err(|_| <Error<T>>::CollectionUnknown)?;13311332 match collection.mode {1333 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1334 _ => Err(<Error<T>>::CollectionUnknown.into()),1335 }1336 }13371338 pub fn collection_exists(collection_id: CollectionId) -> bool {1339 <CollectionHandle<T>>::try_get(collection_id).is_ok()1340 }13411342 pub fn get_collection_property(1343 collection_id: CollectionId,1344 key: RmrkProperty,1345 ) -> Result<PropertyValue, DispatchError> {1346 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1347 .get(&Self::rmrk_property_key(key)?)1348 .ok_or(<Error<T>>::CollectionUnknown)?1349 .clone();13501351 Ok(collection_property)1352 }13531354 pub fn get_collection_property_decoded<V: Decode>(1355 collection_id: CollectionId,1356 key: RmrkProperty,1357 ) -> Result<V, DispatchError> {1358 Self::decode_property(&Self::get_collection_property(collection_id, key)?)1359 }13601361 pub fn get_collection_type(1362 collection_id: CollectionId,1363 ) -> Result<misc::CollectionType, DispatchError> {1364 Self::get_collection_property_decoded(collection_id, CollectionType)1365 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1366 }13671368 pub fn ensure_collection_type(1369 collection_id: CollectionId,1370 collection_type: misc::CollectionType,1371 ) -> DispatchResult {1372 let actual_type = Self::get_collection_type(collection_id)?;1373 ensure!(1374 actual_type == collection_type,1375 <CommonError<T>>::NoPermission1376 );13771378 Ok(())1379 }13801381 pub fn get_typed_nft_collection(1382 collection_id: CollectionId,1383 collection_type: misc::CollectionType,1384 ) -> Result<NonfungibleHandle<T>, DispatchError> {1385 Self::ensure_collection_type(collection_id, collection_type)?;13861387 Self::get_nft_collection(collection_id)1388 }13891390 pub fn get_typed_nft_collection_mapped(1391 rmrk_collection_id: RmrkCollectionId,1392 collection_type: misc::CollectionType,1393 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1394 let unique_collection_id = match collection_type {1395 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1396 _ => rmrk_collection_id.into(),1397 };13981399 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;14001401 Ok((collection, unique_collection_id))1402 }14031404 pub fn get_nft_property(1405 collection_id: CollectionId,1406 nft_id: TokenId,1407 key: RmrkProperty,1408 ) -> Result<PropertyValue, DispatchError> {1409 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1410 .get(&Self::rmrk_property_key(key)?)1411 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?1412 .clone();14131414 Ok(nft_property)1415 }14161417 pub fn get_nft_property_decoded<V: Decode>(1418 collection_id: CollectionId,1419 nft_id: TokenId,1420 key: RmrkProperty,1421 ) -> Result<V, DispatchError> {1422 Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)1423 }14241425 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1426 <TokenData<T>>::contains_key((collection_id, nft_id))1427 }14281429 pub fn get_nft_type(1430 collection_id: CollectionId,1431 token_id: TokenId,1432 ) -> Result<NftType, DispatchError> {1433 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1434 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1435 }14361437 pub fn ensure_nft_type(1438 collection_id: CollectionId,1439 token_id: TokenId,1440 nft_type: NftType,1441 ) -> DispatchResult {1442 let actual_type = Self::get_nft_type(collection_id, token_id)?;1443 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);14441445 Ok(())1446 }14471448 pub fn ensure_nft_owner(1449 collection_id: CollectionId,1450 token_id: TokenId,1451 possible_owner: &T::CrossAccountId,1452 nesting_budget: &dyn budget::Budget,1453 ) -> DispatchResult {1454 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1455 possible_owner.clone(),1456 collection_id,1457 token_id,1458 None,1459 nesting_budget,1460 )1461 .map_err(Self::map_unique_err_to_proxy)?;14621463 ensure!(is_owned, <Error<T>>::NoPermission);14641465 Ok(())1466 }14671468 pub fn filter_user_properties<Key, Value, R, Mapper>(1469 collection_id: CollectionId,1470 token_id: Option<TokenId>,1471 filter_keys: Option<Vec<RmrkPropertyKey>>,1472 mapper: Mapper,1473 ) -> Result<Vec<R>, DispatchError>1474 where1475 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1476 Value: Decode + Default,1477 Mapper: Fn(Key, Value) -> R,1478 {1479 filter_keys1480 .map(|keys| {1481 let properties = keys1482 .into_iter()1483 .filter_map(|key| {1484 let key: Key = key.try_into().ok()?;14851486 let value = match token_id {1487 Some(token_id) => Self::get_nft_property_decoded(1488 collection_id,1489 token_id,1490 UserProperty(key.as_ref()),1491 ),1492 None => Self::get_collection_property_decoded(1493 collection_id,1494 UserProperty(key.as_ref()),1495 ),1496 }1497 .ok()?;14981499 Some(mapper(key, value))1500 })1501 .collect();15021503 Ok(properties)1504 })1505 .unwrap_or_else(|| {1506 let properties =1507 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();15081509 Ok(properties)1510 })1511 }15121513 pub fn iterate_user_properties<Key, Value, R, Mapper>(1514 collection_id: CollectionId,1515 token_id: Option<TokenId>,1516 mapper: Mapper,1517 ) -> Result<impl Iterator<Item = R>, DispatchError>1518 where1519 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1520 Value: Decode + Default,1521 Mapper: Fn(Key, Value) -> R,1522 {1523 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;15241525 let properties = match token_id {1526 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1527 None => <PalletCommon<T>>::collection_properties(collection_id),1528 };15291530 let properties = properties.into_iter().filter_map(move |(key, value)| {1531 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;15321533 let key: Key = key.to_vec().try_into().ok()?;1534 let value: Value = value.decode().ok()?;15351536 Some(mapper(key, value))1537 });15381539 Ok(properties)1540 }15411542 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1543 map_unique_err_to_proxy! {1544 match err {1545 CommonError::NoPermission => NoPermission,1546 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1547 CommonError::PublicMintingNotAllowed => NoPermission,1548 CommonError::TokenNotFound => NoAvailableNftId,1549 CommonError::ApprovedValueTooLow => NoPermission,1550 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1551 StructureError::TokenNotFound => NoAvailableNftId,1552 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1553 }1554 }1555 }1556}1// 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, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod misc;37pub mod property;38pub mod rpc;39pub mod weights;4041pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4243use weights::WeightInfo;44use misc::*;45pub use property::*;4647use RmrkProperty::*;4849pub const NESTING_BUDGET: u32 = 5;5051#[frame_support::pallet]52pub mod pallet {53 use super::*;54 use pallet_evm::account;5556 #[pallet::config]57 pub trait Config:58 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config59 {60 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;61 type WeightInfo: WeightInfo;62 }6364 #[pallet::storage]65 #[pallet::getter(fn collection_index)]66 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;6768 #[pallet::storage]69 pub type UniqueCollectionId<T: Config> =70 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;7172 #[pallet::pallet]73 #[pallet::generate_store(pub(super) trait Store)]74 pub struct Pallet<T>(_);7576 #[pallet::event]77 #[pallet::generate_deposit(pub(super) fn deposit_event)]78 pub enum Event<T: Config> {79 CollectionCreated {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 CollectionDestroyed {84 issuer: T::AccountId,85 collection_id: RmrkCollectionId,86 },87 IssuerChanged {88 old_issuer: T::AccountId,89 new_issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 CollectionLocked {93 issuer: T::AccountId,94 collection_id: RmrkCollectionId,95 },96 NftMinted {97 owner: T::AccountId,98 collection_id: RmrkCollectionId,99 nft_id: RmrkNftId,100 },101 NFTBurned {102 owner: T::AccountId,103 nft_id: RmrkNftId,104 },105 NFTSent {106 sender: T::AccountId,107 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,108 collection_id: RmrkCollectionId,109 nft_id: RmrkNftId,110 approval_required: bool,111 },112 NFTAccepted {113 sender: T::AccountId,114 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,115 collection_id: RmrkCollectionId,116 nft_id: RmrkNftId,117 },118 NFTRejected {119 sender: T::AccountId,120 collection_id: RmrkCollectionId,121 nft_id: RmrkNftId,122 },123 PropertySet {124 collection_id: RmrkCollectionId,125 maybe_nft_id: Option<RmrkNftId>,126 key: RmrkKeyString,127 value: RmrkValueString,128 },129 ResourceAdded {130 nft_id: RmrkNftId,131 resource_id: RmrkResourceId,132 },133 ResourceRemoval {134 nft_id: RmrkNftId,135 resource_id: RmrkResourceId,136 },137 ResourceAccepted {138 nft_id: RmrkNftId,139 resource_id: RmrkResourceId,140 },141 ResourceRemovalAccepted {142 nft_id: RmrkNftId,143 resource_id: RmrkResourceId,144 },145 PrioritySet {146 collection_id: RmrkCollectionId,147 nft_id: RmrkNftId,148 },149 }150151 #[pallet::error]152 pub enum Error<T> {153 /* Unique-specific events */154 CorruptedCollectionType,155 NftTypeEncodeError,156 RmrkPropertyKeyIsTooLong,157 RmrkPropertyValueIsTooLong,158 RmrkPropertyIsNotFound,159 UnableToDecodeRmrkData,160161 /* RMRK compatible events */162 CollectionNotEmpty,163 NoAvailableCollectionId,164 NoAvailableNftId,165 CollectionUnknown,166 NoPermission,167 NonTransferable,168 CollectionFullOrLocked,169 ResourceDoesntExist,170 CannotSendToDescendentOrSelf,171 CannotAcceptNonOwnedNft,172 CannotRejectNonOwnedNft,173 CannotRejectNonPendingNft,174 ResourceNotPending,175 NoAvailableResourceId,176 }177178 #[pallet::call]179 impl<T: Config> Pallet<T> {180 /// Create a collection181 #[transactional]182 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]183 pub fn create_collection(184 origin: OriginFor<T>,185 metadata: RmrkString,186 max: Option<u32>,187 symbol: RmrkCollectionSymbol,188 ) -> DispatchResult {189 let sender = ensure_signed(origin)?;190191 let limits = CollectionLimits {192 owner_can_transfer: Some(false),193 token_limit: max,194 ..Default::default()195 };196197 let data = CreateCollectionData {198 limits: Some(limits),199 token_prefix: symbol200 .into_inner()201 .try_into()202 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,203 permissions: Some(CollectionPermissions {204 nesting: Some(NestingPermissions {205 token_owner: true,206 collection_admin: false,207 restricted: None,208209 permissive: false,210 }),211 ..Default::default()212 }),213 ..Default::default()214 };215216 let unique_collection_id = Self::init_collection(217 T::CrossAccountId::from_sub(sender.clone()),218 data,219 [220 Self::rmrk_property(Metadata, &metadata)?,221 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,222 ]223 .into_iter(),224 )?;225 let rmrk_collection_id = <CollectionIndex<T>>::get();226227 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);228229 <PalletCommon<T>>::set_scoped_collection_property(230 unique_collection_id,231 PropertyScope::Rmrk,232 Self::rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,233 )?;234235 <CollectionIndex<T>>::mutate(|n| *n += 1);236237 Self::deposit_event(Event::CollectionCreated {238 issuer: sender,239 collection_id: rmrk_collection_id,240 });241242 Ok(())243 }244245 /// destroy collection246 #[transactional]247 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]248 pub fn destroy_collection(249 origin: OriginFor<T>,250 collection_id: RmrkCollectionId,251 ) -> DispatchResult {252 let sender = ensure_signed(origin)?;253 let cross_sender = T::CrossAccountId::from_sub(sender.clone());254255 let collection = Self::get_typed_nft_collection(256 Self::unique_collection_id(collection_id)?,257 misc::CollectionType::Regular,258 )?;259 collection.check_is_external()?;260261 <PalletNft<T>>::destroy_collection(collection, &cross_sender)262 .map_err(Self::map_unique_err_to_proxy)?;263264 Self::deposit_event(Event::CollectionDestroyed {265 issuer: sender,266 collection_id,267 });268269 Ok(())270 }271272 /// Change the issuer of a collection273 ///274 /// Parameters:275 /// - `origin`: sender of the transaction276 /// - `collection_id`: collection id of the nft to change issuer of277 /// - `new_issuer`: Collection's new issuer278 #[transactional]279 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]280 pub fn change_collection_issuer(281 origin: OriginFor<T>,282 collection_id: RmrkCollectionId,283 new_issuer: <T::Lookup as StaticLookup>::Source,284 ) -> DispatchResult {285 let sender = ensure_signed(origin)?;286287 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;288 collection.check_is_external()?;289290 let new_issuer = T::Lookup::lookup(new_issuer)?;291292 Self::change_collection_owner(293 Self::unique_collection_id(collection_id)?,294 misc::CollectionType::Regular,295 sender.clone(),296 new_issuer.clone(),297 )?;298299 Self::deposit_event(Event::IssuerChanged {300 old_issuer: sender,301 new_issuer,302 collection_id,303 });304305 Ok(())306 }307308 /// lock collection309 #[transactional]310 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]311 pub fn lock_collection(312 origin: OriginFor<T>,313 collection_id: RmrkCollectionId,314 ) -> DispatchResult {315 let sender = ensure_signed(origin)?;316 let cross_sender = T::CrossAccountId::from_sub(sender.clone());317318 let collection = Self::get_typed_nft_collection(319 Self::unique_collection_id(collection_id)?,320 misc::CollectionType::Regular,321 )?;322 collection.check_is_external()?;323324 Self::check_collection_owner(&collection, &cross_sender)?;325326 let token_count = collection.total_supply();327328 let mut collection = collection.into_inner();329 collection.limits.token_limit = Some(token_count);330 collection.save()?;331332 Self::deposit_event(Event::CollectionLocked {333 issuer: sender,334 collection_id,335 });336337 Ok(())338 }339340 /// Mints an NFT in the specified collection341 /// Sets metadata and the royalty attribute342 ///343 /// Parameters:344 /// - `collection_id`: The class of the asset to be minted.345 /// - `nft_id`: The nft value of the asset to be minted.346 /// - `recipient`: Receiver of the royalty347 /// - `royalty`: Permillage reward from each trade for the Recipient348 /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash349 /// - `transferable`: Ability to transfer this NFT350 #[transactional]351 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]352 pub fn mint_nft(353 origin: OriginFor<T>,354 owner: Option<T::AccountId>,355 collection_id: RmrkCollectionId,356 recipient: Option<T::AccountId>,357 royalty_amount: Option<Permill>,358 metadata: RmrkString,359 transferable: bool,360 resources: Option<BoundedVec<RmrkResourceTypes, MaxResourcesOnMint>>,361 ) -> DispatchResult {362 let sender = ensure_signed(origin)?;363 let cross_sender = T::CrossAccountId::from_sub(sender.clone());364365 let owner = owner.unwrap_or(sender.clone());366 let cross_owner = T::CrossAccountId::from_sub(owner.clone());367368 let collection = Self::get_typed_nft_collection(369 Self::unique_collection_id(collection_id)?,370 misc::CollectionType::Regular,371 )?;372 collection.check_is_external()?;373374 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {375 recipient: recipient.unwrap_or_else(|| owner.clone()),376 amount,377 });378379 let nft_id = Self::create_nft(380 &cross_sender,381 &cross_owner,382 &collection,383 [384 Self::rmrk_property(TokenType, &NftType::Regular)?,385 Self::rmrk_property(Transferable, &transferable)?,386 Self::rmrk_property(PendingNftAccept, &false)?,387 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,388 Self::rmrk_property(Metadata, &metadata)?,389 Self::rmrk_property(Equipped, &false)?,390 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,391 Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,392 ]393 .into_iter(),394 )395 .map_err(|err| match err {396 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),397 err => Self::map_unique_err_to_proxy(err),398 })?;399400 if let Some(resources) = resources {401 for resource in resources {402 Self::resource_add(sender.clone(), collection.id, nft_id, resource)?;403 }404 }405406 Self::deposit_event(Event::NftMinted {407 owner,408 collection_id,409 nft_id: nft_id.0,410 });411412 Ok(())413 }414415 /// burn nft416 #[transactional]417 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]418 pub fn burn_nft(419 origin: OriginFor<T>,420 collection_id: RmrkCollectionId,421 nft_id: RmrkNftId,422 max_burns: u32,423 ) -> DispatchResult {424 let sender = ensure_signed(origin)?;425 let cross_sender = T::CrossAccountId::from_sub(sender.clone());426427 let collection = Self::get_typed_nft_collection(428 Self::unique_collection_id(collection_id)?,429 misc::CollectionType::Regular,430 )?;431 collection.check_is_external()?;432433 Self::destroy_nft(434 cross_sender,435 Self::unique_collection_id(collection_id)?,436 nft_id.into(),437 max_burns,438 <Error<T>>::NoPermission,439 )440 .map_err(|err| Self::map_unique_err_to_proxy(err.error))?;441442 Self::deposit_event(Event::NFTBurned {443 owner: sender,444 nft_id,445 });446447 Ok(())448 }449450 /// Transfers a NFT from an Account or NFT A to another Account or NFT B451 ///452 /// Parameters:453 /// - `origin`: sender of the transaction454 /// - `rmrk_collection_id`: collection id of the nft to be transferred455 /// - `rmrk_nft_id`: nft id of the nft to be transferred456 /// - `new_owner`: new owner of the nft which can be either an account or a NFT457 #[transactional]458 #[pallet::weight(<SelfWeightOf<T>>::send())]459 pub fn send(460 origin: OriginFor<T>,461 rmrk_collection_id: RmrkCollectionId,462 rmrk_nft_id: RmrkNftId,463 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,464 ) -> DispatchResult {465 let sender = ensure_signed(origin.clone())?;466 let cross_sender = T::CrossAccountId::from_sub(sender.clone());467468 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;469 let nft_id = rmrk_nft_id.into();470471 let collection =472 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;473 collection.check_is_external()?;474475 let token_data =476 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;477478 let from = token_data.owner;479480 ensure!(481 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,482 <Error<T>>::NonTransferable483 );484485 ensure!(486 !Self::get_nft_property_decoded(487 collection_id,488 nft_id,489 RmrkProperty::PendingNftAccept490 )?,491 <Error<T>>::NoPermission492 );493494 let target_owner;495 let approval_required;496497 match new_owner {498 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {499 target_owner = T::CrossAccountId::from_sub(account_id.clone());500 approval_required = false;501 }502 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(503 target_collection_id,504 target_nft_id,505 ) => {506 let target_collection_id = Self::unique_collection_id(target_collection_id)?;507508 let target_nft_budget = budget::Value::new(NESTING_BUDGET);509510 let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(511 target_collection_id,512 target_nft_id.into(),513 Some((collection_id, nft_id)),514 &target_nft_budget,515 )516 .map_err(Self::map_unique_err_to_proxy)?;517518 approval_required = cross_sender != target_nft_owner;519520 if approval_required {521 target_owner = target_nft_owner;522523 <PalletNft<T>>::set_scoped_token_property(524 collection.id,525 nft_id,526 PropertyScope::Rmrk,527 Self::rmrk_property(PendingNftAccept, &approval_required)?,528 )?;529 } else {530 target_owner = T::CrossTokenAddressMapping::token_to_address(531 target_collection_id,532 target_nft_id.into(),533 );534 }535 }536 }537538 let src_nft_budget = budget::Value::new(NESTING_BUDGET);539540 <PalletNft<T>>::transfer_from(541 &collection,542 &cross_sender,543 &from,544 &target_owner,545 nft_id,546 &src_nft_budget,547 )548 .map_err(Self::map_unique_err_to_proxy)?;549550 Self::deposit_event(Event::NFTSent {551 sender,552 recipient: new_owner,553 collection_id: rmrk_collection_id,554 nft_id: rmrk_nft_id,555 approval_required,556 });557558 Ok(())559 }560561 /// Accepts an NFT sent from another account to self or owned NFT562 ///563 /// Parameters:564 /// - `origin`: sender of the transaction565 /// - `rmrk_collection_id`: collection id of the nft to be accepted566 /// - `rmrk_nft_id`: nft id of the nft to be accepted567 /// - `new_owner`: either origin's account ID or origin-owned NFT, whichever the NFT was568 /// sent to569 #[transactional]570 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]571 pub fn accept_nft(572 origin: OriginFor<T>,573 rmrk_collection_id: RmrkCollectionId,574 rmrk_nft_id: RmrkNftId,575 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,576 ) -> DispatchResult {577 let sender = ensure_signed(origin.clone())?;578 let cross_sender = T::CrossAccountId::from_sub(sender.clone());579580 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;581 let nft_id = rmrk_nft_id.into();582583 let collection =584 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;585 collection.check_is_external()?;586587 let new_cross_owner = match new_owner {588 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {589 T::CrossAccountId::from_sub(account_id.clone())590 }591 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(592 target_collection_id,593 target_nft_id,594 ) => {595 let target_collection_id = Self::unique_collection_id(target_collection_id)?;596597 T::CrossTokenAddressMapping::token_to_address(598 target_collection_id,599 TokenId(target_nft_id),600 )601 }602 };603604 let budget = budget::Value::new(NESTING_BUDGET);605606 <PalletNft<T>>::transfer(607 &collection,608 &cross_sender,609 &new_cross_owner,610 nft_id,611 &budget,612 )613 .map_err(|err| {614 if err == <CommonError<T>>::UserIsNotAllowedToNest.into() {615 <Error<T>>::CannotAcceptNonOwnedNft.into()616 } else {617 Self::map_unique_err_to_proxy(err)618 }619 })?;620621 <PalletNft<T>>::set_scoped_token_property(622 collection.id,623 nft_id,624 PropertyScope::Rmrk,625 Self::rmrk_property(PendingNftAccept, &false)?,626 )?;627628 Self::deposit_event(Event::NFTAccepted {629 sender,630 recipient: new_owner,631 collection_id: rmrk_collection_id,632 nft_id: rmrk_nft_id,633 });634635 Ok(())636 }637638 /// Rejects an NFT sent from another account to self or owned NFT639 ///640 /// Parameters:641 /// - `origin`: sender of the transaction642 /// - `rmrk_collection_id`: collection id of the nft to be accepted643 /// - `rmrk_nft_id`: nft id of the nft to be accepted644 #[transactional]645 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]646 pub fn reject_nft(647 origin: OriginFor<T>,648 rmrk_collection_id: RmrkCollectionId,649 rmrk_nft_id: RmrkNftId,650 ) -> DispatchResult {651 let sender = ensure_signed(origin)?;652 let cross_sender = T::CrossAccountId::from_sub(sender.clone());653654 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;655 let nft_id = rmrk_nft_id.into();656657 let collection =658 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;659 collection.check_is_external()?;660661 ensure!(662 <TokenData<T>>::get((collection_id, nft_id)).is_some(),663 <Error<T>>::NoAvailableNftId664 );665666 ensure!(667 Self::get_nft_property_decoded(668 collection_id,669 nft_id,670 RmrkProperty::PendingNftAccept671 )?,672 <Error<T>>::CannotRejectNonPendingNft673 );674675 Self::destroy_nft(676 cross_sender,677 collection_id,678 nft_id,679 NESTING_BUDGET,680 <Error<T>>::CannotRejectNonOwnedNft,681 )682 .map_err(|err| Self::map_unique_err_to_proxy(err.error))?;683684 Self::deposit_event(Event::NFTRejected {685 sender,686 collection_id: rmrk_collection_id,687 nft_id: rmrk_nft_id,688 });689690 Ok(())691 }692693 /// accept the addition of a new resource to an existing NFT694 #[transactional]695 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]696 pub fn accept_resource(697 origin: OriginFor<T>,698 rmrk_collection_id: RmrkCollectionId,699 rmrk_nft_id: RmrkNftId,700 resource_id: RmrkResourceId,701 ) -> DispatchResult {702 let sender = ensure_signed(origin)?;703 let cross_sender = T::CrossAccountId::from_sub(sender);704705 let collection_id = Self::unique_collection_id(rmrk_collection_id)706 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;707 let collection =708 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;709 collection.check_is_external()?;710711 let nft_id = rmrk_nft_id.into();712713 let budget = budget::Value::new(NESTING_BUDGET);714715 let nft_owner =716 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)717 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;718719 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {720 ensure!(res.pending, <Error<T>>::ResourceNotPending);721 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);722723 res.pending = false;724725 Ok(())726 })?;727728 Self::deposit_event(Event::<T>::ResourceAccepted {729 nft_id: rmrk_nft_id,730 resource_id,731 });732733 Ok(())734 }735736 /// accept the removal of a resource of an existing NFT737 #[transactional]738 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]739 pub fn accept_resource_removal(740 origin: OriginFor<T>,741 rmrk_collection_id: RmrkCollectionId,742 rmrk_nft_id: RmrkNftId,743 resource_id: RmrkResourceId,744 ) -> DispatchResult {745 let sender = ensure_signed(origin)?;746 let cross_sender = T::CrossAccountId::from_sub(sender);747748 let collection_id = Self::unique_collection_id(rmrk_collection_id)749 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;750 let collection =751 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;752 collection.check_is_external()?;753754 let nft_id = rmrk_nft_id.into();755756 let budget = budget::Value::new(NESTING_BUDGET);757758 let nft_owner =759 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)760 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;761762 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);763764 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;765766 let resource_info = <PalletNft<T>>::token_aux_property((767 collection_id,768 nft_id,769 PropertyScope::Rmrk,770 resource_id_key.clone(),771 ))772 .ok_or(<Error<T>>::ResourceDoesntExist)?;773774 let resource_info: RmrkResourceInfo = Self::decode_property(&resource_info)?;775776 ensure!(777 resource_info.pending_removal,778 <Error<T>>::ResourceNotPending779 );780781 <PalletNft<T>>::remove_token_aux_property(782 collection_id,783 nft_id,784 PropertyScope::Rmrk,785 resource_id_key,786 );787788 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {789 nft_id: rmrk_nft_id,790 resource_id,791 });792793 Ok(())794 }795796 /// set a custom value on an NFT797 #[transactional]798 #[pallet::weight(<SelfWeightOf<T>>::set_property())]799 pub fn set_property(800 origin: OriginFor<T>,801 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,802 maybe_nft_id: Option<RmrkNftId>,803 key: RmrkKeyString,804 value: RmrkValueString,805 ) -> DispatchResult {806 let sender = ensure_signed(origin)?;807 let sender = T::CrossAccountId::from_sub(sender);808809 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;810 let collection =811 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;812 collection.check_is_external()?;813814 let budget = budget::Value::new(NESTING_BUDGET);815816 match maybe_nft_id {817 Some(nft_id) => {818 let token_id: TokenId = nft_id.into();819820 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;821 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;822823 <PalletNft<T>>::set_scoped_token_property(824 collection_id,825 token_id,826 PropertyScope::Rmrk,827 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,828 )?;829 }830 None => {831 let collection = Self::get_typed_nft_collection(832 collection_id,833 misc::CollectionType::Regular,834 )?;835836 Self::check_collection_owner(&collection, &sender)?;837838 <PalletCommon<T>>::set_scoped_collection_property(839 collection_id,840 PropertyScope::Rmrk,841 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,842 )?;843 }844 }845846 Self::deposit_event(Event::PropertySet {847 collection_id: rmrk_collection_id,848 maybe_nft_id,849 key,850 value,851 });852853 Ok(())854 }855856 /// set a different order of resource priority857 #[transactional]858 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]859 pub fn set_priority(860 origin: OriginFor<T>,861 rmrk_collection_id: RmrkCollectionId,862 rmrk_nft_id: RmrkNftId,863 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,864 ) -> DispatchResult {865 let sender = ensure_signed(origin)?;866 let sender = T::CrossAccountId::from_sub(sender);867868 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;869 let nft_id = rmrk_nft_id.into();870871 let collection =872 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;873 collection.check_is_external()?;874875 let budget = budget::Value::new(NESTING_BUDGET);876877 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;878 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;879880 <PalletNft<T>>::set_scoped_token_property(881 collection_id,882 nft_id,883 PropertyScope::Rmrk,884 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,885 )?;886887 Self::deposit_event(Event::<T>::PrioritySet {888 collection_id: rmrk_collection_id,889 nft_id: rmrk_nft_id,890 });891892 Ok(())893 }894895 /// Create basic resource896 #[transactional]897 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]898 pub fn add_basic_resource(899 origin: OriginFor<T>,900 rmrk_collection_id: RmrkCollectionId,901 nft_id: RmrkNftId,902 resource: RmrkBasicResource,903 ) -> DispatchResult {904 let sender = ensure_signed(origin.clone())?;905906 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;907 let collection =908 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;909 collection.check_is_external()?;910911 let resource_id = Self::resource_add(912 sender,913 collection_id,914 nft_id.into(),915 RmrkResourceTypes::Basic(resource),916 )?;917918 Self::deposit_event(Event::ResourceAdded {919 nft_id,920 resource_id,921 });922 Ok(())923 }924925 /// Create composable resource926 #[transactional]927 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]928 pub fn add_composable_resource(929 origin: OriginFor<T>,930 rmrk_collection_id: RmrkCollectionId,931 nft_id: RmrkNftId,932 resource: RmrkComposableResource,933 ) -> DispatchResult {934 let sender = ensure_signed(origin.clone())?;935936 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;937 let collection =938 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;939 collection.check_is_external()?;940941 let resource_id = Self::resource_add(942 sender,943 collection_id,944 nft_id.into(),945 RmrkResourceTypes::Composable(resource),946 )?;947948 Self::deposit_event(Event::ResourceAdded {949 nft_id,950 resource_id,951 });952 Ok(())953 }954955 /// Create slot resource956 #[transactional]957 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]958 pub fn add_slot_resource(959 origin: OriginFor<T>,960 rmrk_collection_id: RmrkCollectionId,961 nft_id: RmrkNftId,962 resource: RmrkSlotResource,963 ) -> DispatchResult {964 let sender = ensure_signed(origin.clone())?;965966 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;967 let collection =968 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;969 collection.check_is_external()?;970971 let resource_id = Self::resource_add(972 sender,973 collection_id,974 nft_id.into(),975 RmrkResourceTypes::Slot(resource),976 )?;977978 Self::deposit_event(Event::ResourceAdded {979 nft_id,980 resource_id,981 });982 Ok(())983 }984985 /// remove resource986 #[transactional]987 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]988 pub fn remove_resource(989 origin: OriginFor<T>,990 rmrk_collection_id: RmrkCollectionId,991 nft_id: RmrkNftId,992 resource_id: RmrkResourceId,993 ) -> DispatchResult {994 let sender = ensure_signed(origin.clone())?;995996 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;997 let collection =998 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;999 collection.check_is_external()?;10001001 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id)?;10021003 Self::deposit_event(Event::ResourceRemoval {1004 nft_id,1005 resource_id,1006 });1007 Ok(())1008 }1009 }1010}10111012impl<T: Config> Pallet<T> {1013 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {1014 let key = rmrk_key.to_key::<T>()?;10151016 let scoped_key = PropertyScope::Rmrk1017 .apply(key)1018 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;10191020 Ok(scoped_key)1021 }10221023 // todo think about renaming these1024 pub fn rmrk_property<E: Encode>(1025 rmrk_key: RmrkProperty,1026 value: &E,1027 ) -> Result<Property, DispatchError> {1028 let key = rmrk_key.to_key::<T>()?;10291030 let value = Self::encode_property(value)?;10311032 let property = Property { key, value };10331034 Ok(property)1035 }10361037 pub fn encode_property<E: Encode, S: Get<u32>>(1038 value: &E,1039 ) -> Result<BoundedBytes<S>, DispatchError> {1040 let value = value1041 .encode()1042 .try_into()1043 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;10441045 Ok(value)1046 }10471048 pub fn decode_property<D: Decode, S: Get<u32>>(1049 vec: &BoundedBytes<S>,1050 ) -> Result<D, DispatchError> {1051 vec.decode()1052 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())1053 }10541055 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1056 where1057 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1058 {1059 vec.rebind()1060 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1061 }10621063 fn init_collection(1064 sender: T::CrossAccountId,1065 data: CreateCollectionData<T::AccountId>,1066 properties: impl Iterator<Item = Property>,1067 ) -> Result<CollectionId, DispatchError> {1068 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10691070 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1071 return Err(<Error<T>>::NoAvailableCollectionId.into());1072 }10731074 <PalletCommon<T>>::set_scoped_collection_properties(1075 collection_id?,1076 PropertyScope::Rmrk,1077 properties,1078 )?;10791080 collection_id1081 }10821083 pub fn create_nft(1084 sender: &T::CrossAccountId,1085 owner: &T::CrossAccountId,1086 collection: &NonfungibleHandle<T>,1087 properties: impl Iterator<Item = Property>,1088 ) -> Result<TokenId, DispatchError> {1089 let data = CreateNftExData {1090 properties: BoundedVec::default(),1091 owner: owner.clone(),1092 };10931094 let budget = budget::Value::new(NESTING_BUDGET);10951096 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10971098 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10991100 <PalletNft<T>>::set_scoped_token_properties(1101 collection.id,1102 nft_id,1103 PropertyScope::Rmrk,1104 properties,1105 )?;11061107 Ok(nft_id)1108 }11091110 fn destroy_nft(1111 sender: T::CrossAccountId,1112 collection_id: CollectionId,1113 token_id: TokenId,1114 max_burns: u32,1115 error_if_not_owned: Error<T>,1116 ) -> DispatchResultWithPostInfo {1117 let collection =1118 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;11191120 let token_data =1121 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;11221123 let from = token_data.owner;11241125 let owner_check_budget = budget::Value::new(NESTING_BUDGET);11261127 ensure!(1128 <PalletStructure<T>>::check_indirectly_owned(1129 sender.clone(),1130 collection_id,1131 token_id,1132 None,1133 &owner_check_budget1134 )?,1135 error_if_not_owned,1136 );11371138 let burns_budget = budget::Value::new(max_burns);1139 let breadth_budget = budget::Value::new(max_burns);11401141 <PalletNft<T>>::burn_recursively(1142 &collection,1143 &from,1144 token_id,1145 &burns_budget,1146 &breadth_budget,1147 )1148 }11491150 fn acquire_next_resource_id(1151 collection_id: CollectionId,1152 nft_id: TokenId,1153 ) -> Result<RmrkResourceId, DispatchError> {1154 let resource_id: RmrkResourceId =1155 Self::get_nft_property_decoded(collection_id, nft_id, NextResourceId)?;11561157 let next_id = resource_id1158 .checked_add(1)1159 .ok_or(<Error<T>>::NoAvailableResourceId)?;11601161 <PalletNft<T>>::set_scoped_token_property(1162 collection_id,1163 nft_id,1164 PropertyScope::Rmrk,1165 Self::rmrk_property(NextResourceId, &next_id)?,1166 )?;11671168 Ok(resource_id)1169 }11701171 fn resource_add(1172 sender: T::AccountId,1173 collection_id: CollectionId,1174 nft_id: TokenId,1175 resource: RmrkResourceTypes,1176 ) -> Result<RmrkResourceId, DispatchError> {1177 let collection =1178 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1179 ensure!(collection.owner == sender, Error::<T>::NoPermission);11801181 let sender = T::CrossAccountId::from_sub(sender);1182 let budget = budget::Value::new(NESTING_BUDGET);11831184 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1185 .map_err(Self::map_unique_err_to_proxy)?;11861187 let pending = sender != nft_owner;11881189 let id = Self::acquire_next_resource_id(collection_id, nft_id)?;11901191 let resource_info = RmrkResourceInfo {1192 id,1193 resource,1194 pending,1195 pending_removal: false,1196 };11971198 <PalletNft<T>>::try_mutate_token_aux_property(1199 collection_id,1200 nft_id,1201 PropertyScope::Rmrk,1202 Self::rmrk_property_key(ResourceId(id))?,1203 |value| -> DispatchResult {1204 *value = Some(Self::encode_property(&resource_info)?);12051206 Ok(())1207 },1208 )?;12091210 Ok(id)1211 }12121213 fn resource_remove(1214 sender: T::AccountId,1215 collection_id: CollectionId,1216 nft_id: TokenId,1217 resource_id: RmrkResourceId,1218 ) -> DispatchResult {1219 let collection =1220 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1221 ensure!(collection.owner == sender, Error::<T>::NoPermission);12221223 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1224 let scope = PropertyScope::Rmrk;12251226 ensure!(1227 <PalletNft<T>>::token_aux_property((1228 collection_id,1229 nft_id,1230 scope,1231 resource_id_key.clone()1232 ))1233 .is_some(),1234 <Error<T>>::ResourceDoesntExist1235 );12361237 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1238 let topmost_owner =1239 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;12401241 let sender = T::CrossAccountId::from_sub(sender);1242 if topmost_owner == sender {1243 <PalletNft<T>>::remove_token_aux_property(1244 collection_id,1245 nft_id,1246 PropertyScope::Rmrk,1247 Self::rmrk_property_key(ResourceId(resource_id))?,1248 );1249 } else {1250 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {1251 res.pending_removal = true;12521253 Ok(())1254 })?;1255 }12561257 Ok(())1258 }12591260 fn try_mutate_resource_info(1261 collection_id: CollectionId,1262 nft_id: TokenId,1263 resource_id: RmrkResourceId,1264 f: impl FnOnce(&mut RmrkResourceInfo) -> DispatchResult,1265 ) -> DispatchResult {1266 <PalletNft<T>>::try_mutate_token_aux_property(1267 collection_id,1268 nft_id,1269 PropertyScope::Rmrk,1270 Self::rmrk_property_key(ResourceId(resource_id))?,1271 |value| match value {1272 Some(value) => {1273 let mut resource_info: RmrkResourceInfo = Self::decode_property(value)?;12741275 f(&mut resource_info)?;12761277 *value = Self::encode_property(&resource_info)?;12781279 Ok(())1280 }1281 None => Err(<Error<T>>::ResourceDoesntExist.into()),1282 },1283 )1284 }12851286 fn change_collection_owner(1287 collection_id: CollectionId,1288 collection_type: misc::CollectionType,1289 sender: T::AccountId,1290 new_owner: T::AccountId,1291 ) -> DispatchResult {1292 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1293 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;12941295 let mut collection = collection.into_inner();12961297 collection.owner = new_owner;1298 collection.save()1299 }13001301 fn check_collection_owner(1302 collection: &NonfungibleHandle<T>,1303 account: &T::CrossAccountId,1304 ) -> DispatchResult {1305 collection1306 .check_is_owner(account)1307 .map_err(Self::map_unique_err_to_proxy)1308 }13091310 pub fn last_collection_idx() -> RmrkCollectionId {1311 <CollectionIndex<T>>::get()1312 }13131314 pub fn unique_collection_id(1315 rmrk_collection_id: RmrkCollectionId,1316 ) -> Result<CollectionId, DispatchError> {1317 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1318 .map_err(|_| <Error<T>>::CollectionUnknown.into())1319 }13201321 pub fn rmrk_collection_id(1322 unique_collection_id: CollectionId,1323 ) -> Result<RmrkCollectionId, DispatchError> {1324 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)1325 }13261327 pub fn get_nft_collection(1328 collection_id: CollectionId,1329 ) -> Result<NonfungibleHandle<T>, DispatchError> {1330 let collection = <CollectionHandle<T>>::try_get(collection_id)1331 .map_err(|_| <Error<T>>::CollectionUnknown)?;13321333 match collection.mode {1334 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1335 _ => Err(<Error<T>>::CollectionUnknown.into()),1336 }1337 }13381339 pub fn collection_exists(collection_id: CollectionId) -> bool {1340 <CollectionHandle<T>>::try_get(collection_id).is_ok()1341 }13421343 pub fn get_collection_property(1344 collection_id: CollectionId,1345 key: RmrkProperty,1346 ) -> Result<PropertyValue, DispatchError> {1347 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1348 .get(&Self::rmrk_property_key(key)?)1349 .ok_or(<Error<T>>::CollectionUnknown)?1350 .clone();13511352 Ok(collection_property)1353 }13541355 pub fn get_collection_property_decoded<V: Decode>(1356 collection_id: CollectionId,1357 key: RmrkProperty,1358 ) -> Result<V, DispatchError> {1359 Self::decode_property(&Self::get_collection_property(collection_id, key)?)1360 }13611362 pub fn get_collection_type(1363 collection_id: CollectionId,1364 ) -> Result<misc::CollectionType, DispatchError> {1365 Self::get_collection_property_decoded(collection_id, CollectionType)1366 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1367 }13681369 pub fn ensure_collection_type(1370 collection_id: CollectionId,1371 collection_type: misc::CollectionType,1372 ) -> DispatchResult {1373 let actual_type = Self::get_collection_type(collection_id)?;1374 ensure!(1375 actual_type == collection_type,1376 <CommonError<T>>::NoPermission1377 );13781379 Ok(())1380 }13811382 pub fn get_typed_nft_collection(1383 collection_id: CollectionId,1384 collection_type: misc::CollectionType,1385 ) -> Result<NonfungibleHandle<T>, DispatchError> {1386 Self::ensure_collection_type(collection_id, collection_type)?;13871388 Self::get_nft_collection(collection_id)1389 }13901391 pub fn get_typed_nft_collection_mapped(1392 rmrk_collection_id: RmrkCollectionId,1393 collection_type: misc::CollectionType,1394 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1395 let unique_collection_id = match collection_type {1396 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1397 _ => rmrk_collection_id.into(),1398 };13991400 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;14011402 Ok((collection, unique_collection_id))1403 }14041405 pub fn get_nft_property(1406 collection_id: CollectionId,1407 nft_id: TokenId,1408 key: RmrkProperty,1409 ) -> Result<PropertyValue, DispatchError> {1410 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1411 .get(&Self::rmrk_property_key(key)?)1412 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?1413 .clone();14141415 Ok(nft_property)1416 }14171418 pub fn get_nft_property_decoded<V: Decode>(1419 collection_id: CollectionId,1420 nft_id: TokenId,1421 key: RmrkProperty,1422 ) -> Result<V, DispatchError> {1423 Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)1424 }14251426 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1427 <TokenData<T>>::contains_key((collection_id, nft_id))1428 }14291430 pub fn get_nft_type(1431 collection_id: CollectionId,1432 token_id: TokenId,1433 ) -> Result<NftType, DispatchError> {1434 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1435 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1436 }14371438 pub fn ensure_nft_type(1439 collection_id: CollectionId,1440 token_id: TokenId,1441 nft_type: NftType,1442 ) -> DispatchResult {1443 let actual_type = Self::get_nft_type(collection_id, token_id)?;1444 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);14451446 Ok(())1447 }14481449 pub fn ensure_nft_owner(1450 collection_id: CollectionId,1451 token_id: TokenId,1452 possible_owner: &T::CrossAccountId,1453 nesting_budget: &dyn budget::Budget,1454 ) -> DispatchResult {1455 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1456 possible_owner.clone(),1457 collection_id,1458 token_id,1459 None,1460 nesting_budget,1461 )1462 .map_err(Self::map_unique_err_to_proxy)?;14631464 ensure!(is_owned, <Error<T>>::NoPermission);14651466 Ok(())1467 }14681469 pub fn filter_user_properties<Key, Value, R, Mapper>(1470 collection_id: CollectionId,1471 token_id: Option<TokenId>,1472 filter_keys: Option<Vec<RmrkPropertyKey>>,1473 mapper: Mapper,1474 ) -> Result<Vec<R>, DispatchError>1475 where1476 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1477 Value: Decode + Default,1478 Mapper: Fn(Key, Value) -> R,1479 {1480 filter_keys1481 .map(|keys| {1482 let properties = keys1483 .into_iter()1484 .filter_map(|key| {1485 let key: Key = key.try_into().ok()?;14861487 let value = match token_id {1488 Some(token_id) => Self::get_nft_property_decoded(1489 collection_id,1490 token_id,1491 UserProperty(key.as_ref()),1492 ),1493 None => Self::get_collection_property_decoded(1494 collection_id,1495 UserProperty(key.as_ref()),1496 ),1497 }1498 .ok()?;14991500 Some(mapper(key, value))1501 })1502 .collect();15031504 Ok(properties)1505 })1506 .unwrap_or_else(|| {1507 let properties =1508 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();15091510 Ok(properties)1511 })1512 }15131514 pub fn iterate_user_properties<Key, Value, R, Mapper>(1515 collection_id: CollectionId,1516 token_id: Option<TokenId>,1517 mapper: Mapper,1518 ) -> Result<impl Iterator<Item = R>, DispatchError>1519 where1520 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1521 Value: Decode + Default,1522 Mapper: Fn(Key, Value) -> R,1523 {1524 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;15251526 let properties = match token_id {1527 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1528 None => <PalletCommon<T>>::collection_properties(collection_id),1529 };15301531 let properties = properties.into_iter().filter_map(move |(key, value)| {1532 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;15331534 let key: Key = key.to_vec().try_into().ok()?;1535 let value: Value = value.decode().ok()?;15361537 Some(mapper(key, value))1538 });15391540 Ok(properties)1541 }15421543 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1544 map_unique_err_to_proxy! {1545 match err {1546 CommonError::NoPermission => NoPermission,1547 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1548 CommonError::PublicMintingNotAllowed => NoPermission,1549 CommonError::TokenNotFound => NoAvailableNftId,1550 CommonError::ApprovedValueTooLow => NoPermission,1551 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1552 StructureError::TokenNotFound => NoAvailableNftId,1553 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1554 }1555 }1556 }1557}pallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/rpc.rs
@@ -0,0 +1,265 @@
+use super::*;
+
+pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {
+ Ok(<Pallet<T>>::last_collection_idx())
+}
+
+pub fn collection_by_id<T: Config>(
+ collection_id: RmrkCollectionId,
+) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {
+ let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(
+ collection_id,
+ misc::CollectionType::Regular,
+ ) {
+ Ok(c) => c,
+ Err(_) => return Ok(None),
+ };
+
+ let nfts_count = collection.total_supply();
+
+ Ok(Some(RmrkCollectionInfo {
+ issuer: collection.owner.clone(),
+ metadata: <Pallet<T>>::get_collection_property_decoded(
+ collection_id,
+ RmrkProperty::Metadata,
+ )?,
+ max: collection.limits.token_limit,
+ symbol: <Pallet<T>>::rebind(&collection.token_prefix)?,
+ nfts_count,
+ }))
+}
+
+pub fn nft_by_id<T: Config>(
+ collection_id: RmrkCollectionId,
+ nft_by_id: RmrkNftId,
+) -> Result<Option<RmrkInstanceInfo<T::AccountId>>, DispatchError> {
+ let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(
+ collection_id,
+ misc::CollectionType::Regular,
+ ) {
+ Ok(c) => c,
+ Err(_) => return Ok(None),
+ };
+
+ let nft_id = TokenId(nft_by_id);
+ if !<Pallet<T>>::nft_exists(collection_id, nft_id) {
+ return Ok(None);
+ }
+
+ let owner = match collection.token_owner(nft_id) {
+ Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {
+ Some((col, tok)) => {
+ let rmrk_collection = <Pallet<T>>::rmrk_collection_id(col)?;
+
+ RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
+ }
+ None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone()),
+ },
+ None => return Ok(None),
+ };
+
+ Ok(Some(RmrkInstanceInfo {
+ owner: owner,
+ royalty: <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ nft_id,
+ RmrkProperty::RoyaltyInfo,
+ )?,
+ metadata: <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ nft_id,
+ RmrkProperty::Metadata,
+ )?,
+ equipped: <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ nft_id,
+ RmrkProperty::Equipped,
+ )?,
+ pending: <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ nft_id,
+ RmrkProperty::PendingNftAccept,
+ )?,
+ }))
+}
+
+pub fn account_tokens<T: Config>(
+ account_id: T::AccountId,
+ collection_id: RmrkCollectionId,
+) -> Result<Vec<RmrkNftId>, DispatchError> {
+ let cross_account_id = CrossAccountId::from_sub(account_id);
+
+ let (collection, collection_id) = match <Pallet<T>>::get_typed_nft_collection_mapped(
+ collection_id,
+ misc::CollectionType::Regular,
+ ) {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
+ };
+
+ let tokens = collection
+ .account_tokens(cross_account_id)
+ .into_iter()
+ .filter(|token| {
+ let is_pending = <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ *token,
+ RmrkProperty::PendingNftAccept,
+ )
+ .unwrap_or(true);
+
+ !is_pending
+ })
+ .map(|token| token.0)
+ .collect();
+
+ Ok(tokens)
+}
+
+pub fn nft_children<T: Config>(
+ collection_id: RmrkCollectionId,
+ nft_id: RmrkNftId,
+) -> Result<Vec<RmrkNftChild>, DispatchError> {
+ let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new()),
+ };
+ let nft_id = TokenId(nft_id);
+ if !<Pallet<T>>::nft_exists(collection_id, nft_id) {
+ return Ok(Vec::new());
+ }
+
+ Ok(
+ pallet_nonfungible::TokenChildren::<T>::iter_prefix((collection_id, nft_id))
+ .filter_map(|((child_collection, child_token), _)| {
+ let is_pending = <Pallet<T>>::get_nft_property_decoded(
+ child_collection,
+ child_token,
+ RmrkProperty::PendingNftAccept,
+ )
+ .ok()?;
+
+ if is_pending {
+ return None;
+ }
+
+ let rmrk_child_collection =
+ <Pallet<T>>::rmrk_collection_id(child_collection).ok()?;
+
+ Some(RmrkNftChild {
+ collection_id: rmrk_child_collection,
+ nft_id: child_token.0,
+ })
+ })
+ .collect(),
+ )
+}
+
+pub fn collection_properties<T: Config>(
+ collection_id: RmrkCollectionId,
+ filter_keys: Option<Vec<RmrkPropertyKey>>,
+) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
+ let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new()),
+ };
+ if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {
+ return Ok(Vec::new());
+ }
+
+ let properties = <Pallet<T>>::filter_user_properties(
+ collection_id,
+ /* token_id = */ None,
+ filter_keys,
+ |key, value| RmrkPropertyInfo { key, value },
+ )?;
+
+ Ok(properties)
+}
+
+pub fn nft_properties<T: Config>(
+ collection_id: RmrkCollectionId,
+ nft_id: RmrkNftId,
+ filter_keys: Option<Vec<RmrkPropertyKey>>,
+) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
+ let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new()),
+ };
+ let token_id = TokenId(nft_id);
+
+ if <Pallet<T>>::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
+ return Ok(Vec::new());
+ }
+
+ let properties = <Pallet<T>>::filter_user_properties(
+ collection_id,
+ Some(token_id),
+ filter_keys,
+ |key, value| RmrkPropertyInfo { key, value },
+ )?;
+
+ Ok(properties)
+}
+
+pub fn nft_resources<T: Config>(
+ collection_id: RmrkCollectionId,
+ nft_id: RmrkNftId,
+) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
+ let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new()),
+ };
+ if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {
+ return Ok(Vec::new());
+ }
+
+ let nft_id = TokenId(nft_id);
+ if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {
+ return Ok(Vec::new());
+ }
+
+ let resources = <pallet_nonfungible::Pallet<T>>::iterate_token_aux_properties(
+ collection_id,
+ nft_id,
+ PropertyScope::Rmrk,
+ )
+ .filter_map(|(_, value)| {
+ let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;
+
+ Some(resource_info)
+ })
+ .collect();
+
+ Ok(resources)
+}
+
+pub fn nft_resource_priority<T: Config>(
+ collection_id: RmrkCollectionId,
+ nft_id: RmrkNftId,
+ resource_id: RmrkResourceId,
+) -> Result<Option<u32>, DispatchError> {
+ let collection_id = match <Pallet<T>>::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None),
+ };
+ if <Pallet<T>>::ensure_collection_type(collection_id, misc::CollectionType::Regular).is_err() {
+ return Ok(None);
+ }
+
+ let nft_id = TokenId(nft_id);
+ if <Pallet<T>>::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() {
+ return Ok(None);
+ }
+
+ let priorities: Vec<_> = <Pallet<T>>::get_nft_property_decoded(
+ collection_id,
+ nft_id,
+ RmrkProperty::ResourcePriorities,
+ )?;
+ Ok(priorities
+ .into_iter()
+ .enumerate()
+ .find(|(_, id)| *id == resource_id)
+ .map(|(priority, _): (usize, RmrkResourceId)| priority as u32))
+}
pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -34,6 +34,7 @@
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
+pub mod rpc;
pub mod weights;
pub type SelfWeightOf<T> = <T as Config>::WeightInfo;
pallets/proxy-rmrk-equip/src/rpc.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-equip/src/rpc.rs
@@ -0,0 +1,186 @@
+use super::*;
+use pallet_rmrk_core::{misc, property::*};
+use sp_std::vec::Vec;
+
+pub fn base<T: Config>(
+ base_id: RmrkBaseId,
+) -> Result<Option<RmrkBaseInfo<T::AccountId>>, DispatchError> {
+ let (collection, collection_id) =
+ match <PalletCore<T>>::get_typed_nft_collection_mapped(base_id, misc::CollectionType::Base)
+ {
+ Ok(c) => c,
+ Err(_) => return Ok(None),
+ };
+
+ Ok(Some(RmrkBaseInfo {
+ issuer: collection.owner.clone(),
+ base_type: <PalletCore<T>>::get_collection_property_decoded(
+ collection_id,
+ RmrkProperty::BaseType,
+ )?,
+ symbol: <PalletCore<T>>::rebind(&collection.token_prefix)?,
+ }))
+}
+
+pub fn base_parts<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
+ use pallet_common::CommonCollectionOperations;
+
+ let (collection, collection_id) =
+ match <PalletCore<T>>::get_typed_nft_collection_mapped(base_id, misc::CollectionType::Base)
+ {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
+ };
+
+ let parts = collection
+ .collection_tokens()
+ .into_iter()
+ .filter_map(|token_id| {
+ let nft_type = <PalletCore<T>>::get_nft_type(collection_id, token_id).ok()?;
+
+ match nft_type {
+ NftType::FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {
+ id: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::ExternalPartId,
+ )
+ .ok()?,
+ src: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::Src,
+ )
+ .ok()?,
+ z: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::ZIndex,
+ )
+ .ok()?,
+ })),
+ NftType::SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {
+ id: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::ExternalPartId,
+ )
+ .ok()?,
+ src: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::Src,
+ )
+ .ok()?,
+ z: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::ZIndex,
+ )
+ .ok()?,
+ equippable: <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::EquippableList,
+ )
+ .ok()?,
+ })),
+ _ => None,
+ }
+ })
+ .collect();
+
+ Ok(parts)
+}
+
+pub fn theme_names<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
+ use pallet_common::CommonCollectionOperations;
+
+ let (collection, collection_id) =
+ match <PalletCore<T>>::get_typed_nft_collection_mapped(base_id, misc::CollectionType::Base)
+ {
+ Ok(c) => c,
+ Err(_) => return Ok(Vec::new()),
+ };
+
+ let theme_names = collection
+ .collection_tokens()
+ .iter()
+ .filter_map(|token_id| {
+ let nft_type = <PalletCore<T>>::get_nft_type(collection_id, *token_id).ok()?;
+
+ match nft_type {
+ NftType::Theme => <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ *token_id,
+ RmrkProperty::ThemeName,
+ )
+ .ok(),
+ _ => None,
+ }
+ })
+ .collect();
+
+ Ok(theme_names)
+}
+
+pub fn theme<T: Config>(
+ base_id: RmrkBaseId,
+ theme_name: RmrkThemeName,
+ filter_keys: Option<Vec<RmrkPropertyKey>>,
+) -> Result<Option<RmrkTheme>, DispatchError> {
+ use pallet_common::CommonCollectionOperations;
+
+ let (collection, collection_id) =
+ match <PalletCore<T>>::get_typed_nft_collection_mapped(base_id, misc::CollectionType::Base)
+ {
+ Ok(c) => c,
+ Err(_) => return Ok(None),
+ };
+
+ let theme_info = collection
+ .collection_tokens()
+ .into_iter()
+ .find_map(|token_id| {
+ <PalletCore<T>>::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;
+
+ let name: RmrkString = <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ token_id,
+ RmrkProperty::ThemeName,
+ )
+ .ok()?;
+
+ if name == theme_name {
+ Some((name, token_id))
+ } else {
+ None
+ }
+ });
+
+ let (name, theme_id) = match theme_info {
+ Some((name, theme_id)) => (name, theme_id),
+ None => return Ok(None),
+ };
+
+ let properties = <PalletCore<T>>::filter_user_properties(
+ collection_id,
+ Some(theme_id),
+ filter_keys,
+ |key, value| RmrkThemeProperty { key, value },
+ )?;
+
+ let inherit = <PalletCore<T>>::get_nft_property_decoded(
+ collection_id,
+ theme_id,
+ RmrkProperty::ThemeInherit,
+ )?;
+
+ let theme = RmrkTheme {
+ name,
+ properties,
+ inherit,
+ };
+
+ Ok(Some(theme))
+}
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -74,9 +74,8 @@
CollectionStats, RpcCollection,
mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},
TokenChild, RmrkCollectionInfo, RmrkInstanceInfo, RmrkResourceInfo, RmrkPropertyInfo,
- RmrkBaseInfo, RmrkPartType, RmrkTheme, RmrkThemeName, RmrkThemeProperty, RmrkCollectionId,
- RmrkNftId, RmrkAccountIdOrCollectionNftTuple, RmrkNftChild, RmrkPropertyKey, RmrkResourceId,
- RmrkBaseId, RmrkFixedPart, RmrkSlotPart, RmrkString,
+ RmrkBaseInfo, RmrkPartType, RmrkTheme, RmrkThemeName, RmrkCollectionId, RmrkNftId,
+ RmrkNftChild, RmrkPropertyKey, RmrkResourceId, RmrkBaseId,
};
// use pallet_contracts::weights::WeightInfo;
@@ -1330,352 +1329,55 @@
RmrkTheme
> for Runtime {
fn last_collection_idx() -> Result<RmrkCollectionId, DispatchError> {
- Ok(RmrkCore::last_collection_idx())
+ pallet_proxy_rmrk_core::rpc::last_collection_idx::<Runtime>()
}
fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let nfts_count = collection.total_supply();
-
- Ok(Some(RmrkCollectionInfo {
- issuer: collection.owner.clone(),
- metadata: RmrkCore::get_collection_property_decoded(collection_id, RmrkProperty::Metadata)?,
- max: collection.limits.token_limit,
- symbol: RmrkCore::rebind(&collection.token_prefix)?,
- nfts_count
- }))
+ pallet_proxy_rmrk_core::rpc::collection_by_id::<Runtime>(collection_id)
}
fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {
- use up_data_structs::mapping::TokenAddressMapping;
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let nft_id = TokenId(nft_by_id);
- if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }
-
- let owner = match collection.token_owner(nft_id) {
- Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
- Some((col, tok)) => {
- let rmrk_collection = RmrkCore::rmrk_collection_id(col)?;
-
- RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
- }
- None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
- },
- None => return Ok(None)
- };
-
- Ok(Some(RmrkInstanceInfo {
- owner: owner,
- royalty: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::RoyaltyInfo)?,
- metadata: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Metadata)?,
- equipped: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Equipped)?,
- pending: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::PendingNftAccept)?,
- }))
+ pallet_proxy_rmrk_core::rpc::nft_by_id::<Runtime>(collection_id, nft_by_id)
}
fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let cross_account_id = CrossAccountId::from_sub(account_id);
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let tokens = collection.account_tokens(cross_account_id)
- .into_iter()
- .filter(|token| {
- let is_pending = RmrkCore::get_nft_property_decoded(
- collection_id,
- *token,
- RmrkProperty::PendingNftAccept
- ).unwrap_or(true);
-
- !is_pending
- })
- .map(|token| token.0)
- .collect();
-
- Ok(tokens)
+ pallet_proxy_rmrk_core::rpc::account_tokens::<Runtime>(account_id, collection_id)
}
fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
- use pallet_proxy_rmrk_core::RmrkProperty;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- let nft_id = TokenId(nft_id);
- if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
-
- Ok(
- pallet_nonfungible::TokenChildren::<Runtime>::iter_prefix((collection_id, nft_id))
- .filter_map(|((child_collection, child_token), _)| {
- let is_pending = RmrkCore::get_nft_property_decoded(
- child_collection,
- child_token,
- RmrkProperty::PendingNftAccept
- ).ok()?;
-
- if is_pending {
- return None;
- }
-
- let rmrk_child_collection = RmrkCore::rmrk_collection_id(
- child_collection
- ).ok()?;
-
- Some(RmrkNftChild {
- collection_id: rmrk_child_collection,
- nft_id: child_token.0,
- })
- }).collect()
- )
+ pallet_proxy_rmrk_core::rpc::nft_children::<Runtime>(collection_id, nft_id)
}
fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::CollectionType;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() {
- return Ok(Vec::new());
- }
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- /* token_id = */ None,
- filter_keys,
- |key, value| RmrkPropertyInfo {
- key,
- value
- }
- )?;
-
- Ok(properties)
+ pallet_proxy_rmrk_core::rpc::collection_properties::<Runtime>(collection_id, filter_keys)
}
fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::NftType;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- let token_id = TokenId(nft_id);
-
- if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
- return Ok(Vec::new());
- }
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- Some(token_id),
- filter_keys,
- |key, value| RmrkPropertyInfo {
- key,
- value
- }
- )?;
-
- Ok(properties)
+ pallet_proxy_rmrk_core::rpc::nft_properties::<Runtime>(collection_id, nft_id, filter_keys)
}
fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::{CollectionType, NftType};
- use up_data_structs::PropertyScope;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
-
- let nft_id = TokenId(nft_id);
- if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() { return Ok(Vec::new()); }
-
- let resources = <pallet_nonfungible::Pallet<Runtime>>::iterate_token_aux_properties(
- collection_id, nft_id, PropertyScope::Rmrk
- ).filter_map(|(_, value)| {
- let resource_info: RmrkResourceInfo = RmrkCore::decode_property(&value).ok()?;
-
- Some(resource_info)
- }).collect();
-
- Ok(resources)
+ pallet_proxy_rmrk_core::rpc::nft_resources::<Runtime>(collection_id, nft_id)
}
fn nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Result<Option<u32>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(None); }
-
- let nft_id = TokenId(nft_id);
- if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() { return Ok(None); }
-
- let priorities: Vec<_> = RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::ResourcePriorities)?;
- Ok(
- priorities.into_iter()
- .enumerate()
- .find(|(_, id)| *id == resource_id)
- .map(|(priority, _): (usize, RmrkResourceId)| priority as u32)
- )
+ pallet_proxy_rmrk_core::rpc::nft_resource_priority::<Runtime>(collection_id, nft_id, resource_id)
}
fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {
- use pallet_proxy_rmrk_core::{
- RmrkProperty, misc::{CollectionType},
- };
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- Ok(Some(RmrkBaseInfo {
- issuer: collection.owner.clone(),
- base_type: RmrkCore::get_collection_property_decoded(collection_id, RmrkProperty::BaseType)?,
- symbol: RmrkCore::rebind(&collection.token_prefix)?,
- }))
+ pallet_proxy_rmrk_equip::rpc::base::<Runtime>(base_id)
}
fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let parts = collection.collection_tokens()
- .into_iter()
- .filter_map(|token_id| {
- let nft_type = RmrkCore::get_nft_type(collection_id, token_id).ok()?;
-
- match nft_type {
- NftType::FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {
- id: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ExternalPartId).ok()?,
- src: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::Src).ok()?,
- z: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ZIndex).ok()?,
- })),
- NftType::SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {
- id: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ExternalPartId).ok()?,
- src: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::Src).ok()?,
- z: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ZIndex).ok()?,
- equippable: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::EquippableList).ok()?,
- })),
- _ => None
- }
- })
- .collect();
-
- Ok(parts)
+ pallet_proxy_rmrk_equip::rpc::base_parts::<Runtime>(base_id)
}
fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{NftType, CollectionType}};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let theme_names = collection.collection_tokens()
- .iter()
- .filter_map(|token_id| {
- let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).ok()?;
-
- match nft_type {
- NftType::Theme => RmrkCore::get_nft_property_decoded(collection_id, *token_id, RmrkProperty::ThemeName).ok(),
- _ => None
- }
- })
- .collect();
-
- Ok(theme_names)
+ pallet_proxy_rmrk_equip::rpc::theme_names::<Runtime>(base_id)
}
fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {
- use pallet_proxy_rmrk_core::{
- RmrkProperty,
- misc::{CollectionType, NftType}
- };
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let theme_info = collection.collection_tokens()
- .into_iter()
- .find_map(|token_id| {
- RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;
-
- let name: RmrkString = RmrkCore::get_nft_property_decoded(
- collection_id, token_id, RmrkProperty::ThemeName
- ).ok()?;
-
- if name == theme_name {
- Some((name, token_id))
- } else {
- None
- }
- });
-
- let (name, theme_id) = match theme_info {
- Some((name, theme_id)) => (name, theme_id),
- None => return Ok(None)
- };
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- Some(theme_id),
- filter_keys,
- |key, value| RmrkThemeProperty {
- key,
- value
- }
- )?;
-
- let inherit = RmrkCore::get_nft_property_decoded(
- collection_id,
- theme_id,
- RmrkProperty::ThemeInherit
- )?;
-
- let theme = RmrkTheme {
- name,
- properties,
- inherit,
- };
-
- Ok(Some(theme))
+ pallet_proxy_rmrk_equip::rpc::theme::<Runtime>(base_id, theme_name, filter_keys)
}
}
}
runtime/quartz/src/lib.rsdiffbeforeafterboth--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -74,9 +74,8 @@
CollectionStats, RpcCollection,
mapping::{EvmTokenAddressMapping, CrossTokenAddressMapping},
TokenChild, RmrkCollectionInfo, RmrkInstanceInfo, RmrkResourceInfo, RmrkPropertyInfo,
- RmrkBaseInfo, RmrkPartType, RmrkTheme, RmrkThemeName, RmrkThemeProperty, RmrkCollectionId,
- RmrkNftId, RmrkAccountIdOrCollectionNftTuple, RmrkNftChild, RmrkPropertyKey, RmrkResourceId,
- RmrkBaseId, RmrkFixedPart, RmrkSlotPart, RmrkString,
+ RmrkBaseInfo, RmrkPartType, RmrkTheme, RmrkThemeName, RmrkCollectionId, RmrkNftId,
+ RmrkNftChild, RmrkPropertyKey, RmrkResourceId, RmrkBaseId,
};
// use pallet_contracts::weights::WeightInfo;
@@ -1330,352 +1329,55 @@
RmrkTheme
> for Runtime {
fn last_collection_idx() -> Result<RmrkCollectionId, DispatchError> {
- Ok(RmrkCore::last_collection_idx())
+ pallet_proxy_rmrk_core::rpc::last_collection_idx::<Runtime>()
}
fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let nfts_count = collection.total_supply();
-
- Ok(Some(RmrkCollectionInfo {
- issuer: collection.owner.clone(),
- metadata: RmrkCore::get_collection_property_decoded(collection_id, RmrkProperty::Metadata)?,
- max: collection.limits.token_limit,
- symbol: RmrkCore::rebind(&collection.token_prefix)?,
- nfts_count
- }))
+ pallet_proxy_rmrk_core::rpc::collection_by_id::<Runtime>(collection_id)
}
fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {
- use up_data_structs::mapping::TokenAddressMapping;
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let nft_id = TokenId(nft_by_id);
- if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }
-
- let owner = match collection.token_owner(nft_id) {
- Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
- Some((col, tok)) => {
- let rmrk_collection = RmrkCore::rmrk_collection_id(col)?;
-
- RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
- }
- None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
- },
- None => return Ok(None)
- };
-
- Ok(Some(RmrkInstanceInfo {
- owner: owner,
- royalty: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::RoyaltyInfo)?,
- metadata: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Metadata)?,
- equipped: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Equipped)?,
- pending: RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::PendingNftAccept)?,
- }))
+ pallet_proxy_rmrk_core::rpc::nft_by_id::<Runtime>(collection_id, nft_by_id)
}
fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
- use pallet_common::CommonCollectionOperations;
-
- let cross_account_id = CrossAccountId::from_sub(account_id);
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let tokens = collection.account_tokens(cross_account_id)
- .into_iter()
- .filter(|token| {
- let is_pending = RmrkCore::get_nft_property_decoded(
- collection_id,
- *token,
- RmrkProperty::PendingNftAccept
- ).unwrap_or(true);
-
- !is_pending
- })
- .map(|token| token.0)
- .collect();
-
- Ok(tokens)
+ pallet_proxy_rmrk_core::rpc::account_tokens::<Runtime>(account_id, collection_id)
}
fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
- use pallet_proxy_rmrk_core::RmrkProperty;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- let nft_id = TokenId(nft_id);
- if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
-
- Ok(
- pallet_nonfungible::TokenChildren::<Runtime>::iter_prefix((collection_id, nft_id))
- .filter_map(|((child_collection, child_token), _)| {
- let is_pending = RmrkCore::get_nft_property_decoded(
- child_collection,
- child_token,
- RmrkProperty::PendingNftAccept
- ).ok()?;
-
- if is_pending {
- return None;
- }
-
- let rmrk_child_collection = RmrkCore::rmrk_collection_id(
- child_collection
- ).ok()?;
-
- Some(RmrkNftChild {
- collection_id: rmrk_child_collection,
- nft_id: child_token.0,
- })
- }).collect()
- )
+ pallet_proxy_rmrk_core::rpc::nft_children::<Runtime>(collection_id, nft_id)
}
fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::CollectionType;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() {
- return Ok(Vec::new());
- }
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- /* token_id = */ None,
- filter_keys,
- |key, value| RmrkPropertyInfo {
- key,
- value
- }
- )?;
-
- Ok(properties)
+ pallet_proxy_rmrk_core::rpc::collection_properties::<Runtime>(collection_id, filter_keys)
}
fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::NftType;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- let token_id = TokenId(nft_id);
-
- if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
- return Ok(Vec::new());
- }
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- Some(token_id),
- filter_keys,
- |key, value| RmrkPropertyInfo {
- key,
- value
- }
- )?;
-
- Ok(properties)
+ pallet_proxy_rmrk_core::rpc::nft_properties::<Runtime>(collection_id, nft_id, filter_keys)
}
fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
- use pallet_proxy_rmrk_core::misc::{CollectionType, NftType};
- use up_data_structs::PropertyScope;
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(Vec::new())
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
-
- let nft_id = TokenId(nft_id);
- if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() { return Ok(Vec::new()); }
-
- let resources = <pallet_nonfungible::Pallet<Runtime>>::iterate_token_aux_properties(
- collection_id, nft_id, PropertyScope::Rmrk
- ).filter_map(|(_, value)| {
- let resource_info: RmrkResourceInfo = RmrkCore::decode_property(&value).ok()?;
-
- Some(resource_info)
- }).collect();
-
- Ok(resources)
+ pallet_proxy_rmrk_core::rpc::nft_resources::<Runtime>(collection_id, nft_id)
}
fn nft_resource_priority(collection_id: RmrkCollectionId, nft_id: RmrkNftId, resource_id: RmrkResourceId) -> Result<Option<u32>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
-
- let collection_id = match RmrkCore::unique_collection_id(collection_id) {
- Ok(id) => id,
- Err(_) => return Ok(None)
- };
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(None); }
-
- let nft_id = TokenId(nft_id);
- if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Regular).is_err() { return Ok(None); }
-
- let priorities: Vec<_> = RmrkCore::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::ResourcePriorities)?;
- Ok(
- priorities.into_iter()
- .enumerate()
- .find(|(_, id)| *id == resource_id)
- .map(|(priority, _): (usize, RmrkResourceId)| priority as u32)
- )
+ pallet_proxy_rmrk_core::rpc::nft_resource_priority::<Runtime>(collection_id, nft_id, resource_id)
}
fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {
- use pallet_proxy_rmrk_core::{
- RmrkProperty, misc::{CollectionType},
- };
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- Ok(Some(RmrkBaseInfo {
- issuer: collection.owner.clone(),
- base_type: RmrkCore::get_collection_property_decoded(collection_id, RmrkProperty::BaseType)?,
- symbol: RmrkCore::rebind(&collection.token_prefix)?,
- }))
+ pallet_proxy_rmrk_equip::rpc::base::<Runtime>(base_id)
}
fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let parts = collection.collection_tokens()
- .into_iter()
- .filter_map(|token_id| {
- let nft_type = RmrkCore::get_nft_type(collection_id, token_id).ok()?;
-
- match nft_type {
- NftType::FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {
- id: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ExternalPartId).ok()?,
- src: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::Src).ok()?,
- z: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ZIndex).ok()?,
- })),
- NftType::SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {
- id: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ExternalPartId).ok()?,
- src: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::Src).ok()?,
- z: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::ZIndex).ok()?,
- equippable: RmrkCore::get_nft_property_decoded(collection_id, token_id, RmrkProperty::EquippableList).ok()?,
- })),
- _ => None
- }
- })
- .collect();
-
- Ok(parts)
+ pallet_proxy_rmrk_equip::rpc::base_parts::<Runtime>(base_id)
}
fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{NftType, CollectionType}};
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
- };
-
- let theme_names = collection.collection_tokens()
- .iter()
- .filter_map(|token_id| {
- let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).ok()?;
-
- match nft_type {
- NftType::Theme => RmrkCore::get_nft_property_decoded(collection_id, *token_id, RmrkProperty::ThemeName).ok(),
- _ => None
- }
- })
- .collect();
-
- Ok(theme_names)
+ pallet_proxy_rmrk_equip::rpc::theme_names::<Runtime>(base_id)
}
fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {
- use pallet_proxy_rmrk_core::{
- RmrkProperty,
- misc::{CollectionType, NftType}
- };
- use pallet_common::CommonCollectionOperations;
-
- let (collection, collection_id) = match RmrkCore::get_typed_nft_collection_mapped(base_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
- };
-
- let theme_info = collection.collection_tokens()
- .into_iter()
- .find_map(|token_id| {
- RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;
-
- let name: RmrkString = RmrkCore::get_nft_property_decoded(
- collection_id, token_id, RmrkProperty::ThemeName
- ).ok()?;
-
- if name == theme_name {
- Some((name, token_id))
- } else {
- None
- }
- });
-
- let (name, theme_id) = match theme_info {
- Some((name, theme_id)) => (name, theme_id),
- None => return Ok(None)
- };
-
- let properties = RmrkCore::filter_user_properties(
- collection_id,
- Some(theme_id),
- filter_keys,
- |key, value| RmrkThemeProperty {
- key,
- value
- }
- )?;
-
- let inherit = RmrkCore::get_nft_property_decoded(
- collection_id,
- theme_id,
- RmrkProperty::ThemeInherit
- )?;
-
- let theme = RmrkTheme {
- name,
- properties,
- inherit,
- };
-
- Ok(Some(theme))
+ pallet_proxy_rmrk_equip::rpc::theme::<Runtime>(base_id, theme_name, filter_keys)
}
}
}