difftreelog
refactor hide permissive unless benchmarking
in: master
5 files changed
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -98,6 +98,7 @@
token_owner: false,
collection_admin: false,
restricted: None,
+ #[cfg(feature = "runtime-benchmarks")]
permissive: true,
}),
mint_mode: Some(true),
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1212,14 +1212,7 @@
limit_default_clone!(old_limit, new_limit,
access => {},
mint_mode => {},
- nesting => {
- #[cfg(not(feature = "runtime-benchmarks"))]
- ensure!(
- // Permissive is only allowed for tests and internal usage of chain for now
- old_limit.permissive || !new_limit.permissive,
- <Error<T>>::NoPermission,
- )
- },
+ nesting => { /* todo check for permissive, if only it gets out of benchmarks */ },
);
Ok(new_limit)
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -1038,7 +1038,13 @@
nesting_budget: &dyn Budget,
) -> DispatchResult {
let nesting = handle.permissions.nesting();
- if nesting.permissive {
+
+ #[cfg(not(feature = "runtime-benchmarks"))]
+ let permissive = false;
+ #[cfg(feature = "runtime-benchmarks")]
+ let permissive = nesting.permissive;
+
+ if permissive {
// Pass
} else if nesting.token_owner
&& <PalletStructure<T>>::check_indirectly_owned(
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 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 pub 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).map_err(|err| {1366 if err != <Error<T>>::CollectionUnknown.into() {1367 <Error<T>>::CorruptedCollectionType.into()1368 } else {1369 err1370 }1371 })1372 }13731374 pub fn ensure_collection_type(1375 collection_id: CollectionId,1376 collection_type: misc::CollectionType,1377 ) -> DispatchResult {1378 let actual_type = Self::get_collection_type(collection_id)?;1379 ensure!(1380 actual_type == collection_type,1381 <CommonError<T>>::NoPermission1382 );13831384 Ok(())1385 }13861387 pub fn get_typed_nft_collection(1388 collection_id: CollectionId,1389 collection_type: misc::CollectionType,1390 ) -> Result<NonfungibleHandle<T>, DispatchError> {1391 Self::ensure_collection_type(collection_id, collection_type)?;13921393 Self::get_nft_collection(collection_id)1394 }13951396 pub fn get_typed_nft_collection_mapped(1397 rmrk_collection_id: RmrkCollectionId,1398 collection_type: misc::CollectionType,1399 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1400 let unique_collection_id = match collection_type {1401 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1402 _ => rmrk_collection_id.into(),1403 };14041405 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;14061407 Ok((collection, unique_collection_id))1408 }14091410 pub fn get_nft_property(1411 collection_id: CollectionId,1412 nft_id: TokenId,1413 key: RmrkProperty,1414 ) -> Result<PropertyValue, DispatchError> {1415 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1416 .get(&Self::rmrk_property_key(key)?)1417 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?1418 .clone();14191420 Ok(nft_property)1421 }14221423 pub fn get_nft_property_decoded<V: Decode>(1424 collection_id: CollectionId,1425 nft_id: TokenId,1426 key: RmrkProperty,1427 ) -> Result<V, DispatchError> {1428 Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)1429 }14301431 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1432 <TokenData<T>>::contains_key((collection_id, nft_id))1433 }14341435 pub fn get_nft_type(1436 collection_id: CollectionId,1437 token_id: TokenId,1438 ) -> Result<NftType, DispatchError> {1439 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1440 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1441 }14421443 pub fn ensure_nft_type(1444 collection_id: CollectionId,1445 token_id: TokenId,1446 nft_type: NftType,1447 ) -> DispatchResult {1448 let actual_type = Self::get_nft_type(collection_id, token_id)?;1449 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);14501451 Ok(())1452 }14531454 pub fn ensure_nft_owner(1455 collection_id: CollectionId,1456 token_id: TokenId,1457 possible_owner: &T::CrossAccountId,1458 nesting_budget: &dyn budget::Budget,1459 ) -> DispatchResult {1460 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1461 possible_owner.clone(),1462 collection_id,1463 token_id,1464 None,1465 nesting_budget,1466 )1467 .map_err(Self::map_unique_err_to_proxy)?;14681469 ensure!(is_owned, <Error<T>>::NoPermission);14701471 Ok(())1472 }14731474 pub fn filter_user_properties<Key, Value, R, Mapper>(1475 collection_id: CollectionId,1476 token_id: Option<TokenId>,1477 filter_keys: Option<Vec<RmrkPropertyKey>>,1478 mapper: Mapper,1479 ) -> Result<Vec<R>, DispatchError>1480 where1481 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1482 Value: Decode + Default,1483 Mapper: Fn(Key, Value) -> R,1484 {1485 filter_keys1486 .map(|keys| {1487 let properties = keys1488 .into_iter()1489 .filter_map(|key| {1490 let key: Key = key.try_into().ok()?;14911492 let value = match token_id {1493 Some(token_id) => Self::get_nft_property_decoded(1494 collection_id,1495 token_id,1496 UserProperty(key.as_ref()),1497 ),1498 None => Self::get_collection_property_decoded(1499 collection_id,1500 UserProperty(key.as_ref()),1501 ),1502 }1503 .ok()?;15041505 Some(mapper(key, value))1506 })1507 .collect();15081509 Ok(properties)1510 })1511 .unwrap_or_else(|| {1512 let properties =1513 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();15141515 Ok(properties)1516 })1517 }15181519 pub fn iterate_user_properties<Key, Value, R, Mapper>(1520 collection_id: CollectionId,1521 token_id: Option<TokenId>,1522 mapper: Mapper,1523 ) -> Result<impl Iterator<Item = R>, DispatchError>1524 where1525 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1526 Value: Decode + Default,1527 Mapper: Fn(Key, Value) -> R,1528 {1529 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;15301531 let properties = match token_id {1532 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1533 None => <PalletCommon<T>>::collection_properties(collection_id),1534 };15351536 let properties = properties.into_iter().filter_map(move |(key, value)| {1537 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;15381539 let key: Key = key.to_vec().try_into().ok()?;1540 let value: Value = value.decode().ok()?;15411542 Some(mapper(key, value))1543 });15441545 Ok(properties)1546 }15471548 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1549 map_unique_err_to_proxy! {1550 match err {1551 CommonError::NoPermission => NoPermission,1552 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1553 CommonError::PublicMintingNotAllowed => NoPermission,1554 CommonError::TokenNotFound => NoAvailableNftId,1555 CommonError::ApprovedValueTooLow => NoPermission,1556 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1557 StructureError::TokenNotFound => NoAvailableNftId,1558 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1559 }1560 }1561 }1562}primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -454,7 +454,7 @@
token_owner: false,
collection_admin: false,
restricted: None,
-
+ #[cfg(feature = "runtime-benchmarks")]
permissive: false,
};
self.nesting.as_ref().unwrap_or(&DEFAULT)
@@ -499,6 +499,7 @@
/// If set - only tokens from specified collections can be nested
pub restricted: Option<OwnerRestrictedSet>,
+ #[cfg(feature = "runtime-benchmarks")]
/// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`
pub permissive: bool,
}