difftreelog
feat rmrk-core benchmarking
in: master
11 files changed
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -93,5 +93,9 @@
bench-structure:
make _bench PALLET=structure
+.PHONY: bench-rmrk-core
+bench-rmrk-core:
+ make _bench PALLET=proxy-rmrk-core
+
.PHONY: bench
bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -109,7 +109,7 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- |owner, data| <Pallet<T>>::init_collection(owner, data),
+ |owner, data| <Pallet<T>>::init_collection(owner, data, true),
|h| h,
)
}
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -427,7 +427,7 @@
/// Target collection doesn't supports this operation
UnsupportedOperation,
- /// Not sufficient founds to perform action
+ /// Not sufficient funds to perform action
NotSufficientFounds,
/// Collection has nesting disabled
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -51,7 +51,7 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- <Pallet<T>>::init_collection,
+ |owner, data| <Pallet<T>>::init_collection(owner, data, true),
NonfungibleHandle::cast,
)
}
@@ -99,7 +99,7 @@
sender: cross_from_sub(owner); burner: cross_sub;
};
let item = create_max_item(&collection, &sender, burner.clone())?;
- }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+ }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
burn_recursively_breadth_plus_self_plus_self_per_each_raw {
let b in 0..200;
@@ -111,7 +111,7 @@
for i in 0..b {
create_max_item(&collection, &sender, T::CrossTokenAddressMapping::token_to_address(collection.id, item))?;
}
- }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+ }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
transfer {
bench_init!{
@@ -183,7 +183,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?}
+ }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?}
delete_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
@@ -205,7 +205,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- <Pallet<T>>::set_token_properties(&collection, &owner, item, props)?;
+ <Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?;
let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}
}
pallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/benchmarking.rs
@@ -0,0 +1,26 @@
+use sp_std::vec;
+
+use frame_benchmarking::{benchmarks, account};
+use frame_system::RawOrigin;
+use frame_support::{
+ traits::{Currency, Get},
+ BoundedVec,
+};
+
+use crate::{Config, Pallet, Call};
+
+const SEED: u32 = 1;
+
+fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {
+ vec![0; S::get() as usize].try_into().expect("size == S")
+}
+
+benchmarks! {
+ create_collection {
+ let caller = account("caller", 0, SEED);
+ <T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+ let metadata = create_data();
+ // TODO: Fix CollectionTokenPrefixLimitExceeded with create_data
+ let symbol = vec![].try_into().expect("0 <= x");
+ }: _(RawOrigin::Signed(caller), metadata, None, symbol)
+}
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::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46 use super::*;47 use pallet_evm::account;4849 #[pallet::config]50 pub trait Config:51 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52 {53 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54 }5556 #[pallet::storage]57 #[pallet::getter(fn collection_index)]58 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960 #[pallet::storage]61 pub type UniqueCollectionId<T: Config> =62 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364 #[pallet::storage]65 pub type RmrkInernalCollectionId<T: Config> =66 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768 #[pallet::pallet]69 #[pallet::generate_store(pub(super) trait Store)]70 pub struct Pallet<T>(_);7172 #[pallet::event]73 #[pallet::generate_deposit(pub(super) fn deposit_event)]74 pub enum Event<T: Config> {75 CollectionCreated {76 issuer: T::AccountId,77 collection_id: RmrkCollectionId,78 },79 CollectionDestroyed {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 IssuerChanged {84 old_issuer: T::AccountId,85 new_issuer: T::AccountId,86 collection_id: RmrkCollectionId,87 },88 CollectionLocked {89 issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 NftMinted {93 owner: T::AccountId,94 collection_id: RmrkCollectionId,95 nft_id: RmrkNftId,96 },97 NFTBurned {98 owner: T::AccountId,99 nft_id: RmrkNftId,100 },101 NFTSent {102 sender: T::AccountId,103 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,104 collection_id: RmrkCollectionId,105 nft_id: RmrkNftId,106 approval_required: bool,107 },108 NFTAccepted {109 sender: T::AccountId,110 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111 collection_id: RmrkCollectionId,112 nft_id: RmrkNftId,113 },114 NFTRejected {115 sender: T::AccountId,116 collection_id: RmrkCollectionId,117 nft_id: RmrkNftId,118 },119 PropertySet {120 collection_id: RmrkCollectionId,121 maybe_nft_id: Option<RmrkNftId>,122 key: RmrkKeyString,123 value: RmrkValueString,124 },125 ResourceAdded {126 nft_id: RmrkNftId,127 resource_id: RmrkResourceId,128 },129 ResourceRemoval {130 nft_id: RmrkNftId,131 resource_id: RmrkResourceId,132 },133 ResourceAccepted {134 nft_id: RmrkNftId,135 resource_id: RmrkResourceId,136 },137 ResourceRemovalAccepted {138 nft_id: RmrkNftId,139 resource_id: RmrkResourceId,140 },141 PrioritySet {142 collection_id: RmrkCollectionId,143 nft_id: RmrkNftId,144 },145 }146147 #[pallet::error]148 pub enum Error<T> {149 /* Unique-specific events */150 CorruptedCollectionType,151 NftTypeEncodeError,152 RmrkPropertyKeyIsTooLong,153 RmrkPropertyValueIsTooLong,154155 /* RMRK compatible events */156 CollectionNotEmpty,157 NoAvailableCollectionId,158 NoAvailableNftId,159 CollectionUnknown,160 NoPermission,161 NonTransferable,162 CollectionFullOrLocked,163 ResourceDoesntExist,164 CannotSendToDescendentOrSelf,165 CannotAcceptNonOwnedNft,166 CannotRejectNonOwnedNft,167 ResourceNotPending,168 }169170 #[pallet::call]171 impl<T: Config> Pallet<T> {172 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]173 #[transactional]174 pub fn create_collection(175 origin: OriginFor<T>,176 metadata: RmrkString,177 max: Option<u32>,178 symbol: RmrkCollectionSymbol,179 ) -> DispatchResult {180 let sender = ensure_signed(origin)?;181182 let limits = CollectionLimits {183 owner_can_transfer: Some(false),184 token_limit: max,185 ..Default::default()186 };187188 let data = CreateCollectionData {189 limits: Some(limits),190 token_prefix: symbol191 .into_inner()192 .try_into()193 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,194 permissions: Some(CollectionPermissions {195 nesting: Some(NestingRule::Owner),196 ..Default::default()197 }),198 ..Default::default()199 };200201 let unique_collection_id = Self::init_collection(202 T::CrossAccountId::from_sub(sender.clone()),203 data,204 [205 Self::rmrk_property(Metadata, &metadata)?,206 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,207 ]208 .into_iter(),209 )?;210 let rmrk_collection_id = <CollectionIndex<T>>::get();211212 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);213 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);214215 <CollectionIndex<T>>::mutate(|n| *n += 1);216217 Self::deposit_event(Event::CollectionCreated {218 issuer: sender,219 collection_id: rmrk_collection_id,220 });221222 Ok(())223 }224225 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]226 #[transactional]227 pub fn destroy_collection(228 origin: OriginFor<T>,229 collection_id: RmrkCollectionId,230 ) -> DispatchResult {231 let sender = ensure_signed(origin)?;232 let cross_sender = T::CrossAccountId::from_sub(sender.clone());233234 let collection = Self::get_typed_nft_collection(235 Self::unique_collection_id(collection_id)?,236 misc::CollectionType::Regular,237 )?;238 collection.check_is_external()?;239240 <PalletNft<T>>::destroy_collection(collection, &cross_sender)241 .map_err(Self::map_unique_err_to_proxy)?;242243 Self::deposit_event(Event::CollectionDestroyed {244 issuer: sender,245 collection_id,246 });247248 Ok(())249 }250251 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]252 #[transactional]253 pub fn change_collection_issuer(254 origin: OriginFor<T>,255 collection_id: RmrkCollectionId,256 new_issuer: <T::Lookup as StaticLookup>::Source,257 ) -> DispatchResult {258 let sender = ensure_signed(origin)?;259260 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;261 collection.check_is_external()?;262263 let new_issuer = T::Lookup::lookup(new_issuer)?;264265 Self::change_collection_owner(266 Self::unique_collection_id(collection_id)?,267 misc::CollectionType::Regular,268 sender.clone(),269 new_issuer.clone(),270 )?;271272 Self::deposit_event(Event::IssuerChanged {273 old_issuer: sender,274 new_issuer,275 collection_id,276 });277278 Ok(())279 }280281 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]282 #[transactional]283 pub fn lock_collection(284 origin: OriginFor<T>,285 collection_id: RmrkCollectionId,286 ) -> DispatchResult {287 let sender = ensure_signed(origin)?;288 let cross_sender = T::CrossAccountId::from_sub(sender.clone());289290 let collection = Self::get_typed_nft_collection(291 Self::unique_collection_id(collection_id)?,292 misc::CollectionType::Regular,293 )?;294 collection.check_is_external()?;295296 Self::check_collection_owner(&collection, &cross_sender)?;297298 let token_count = collection.total_supply();299300 let mut collection = collection.into_inner();301 collection.limits.token_limit = Some(token_count);302 collection.save()?;303304 Self::deposit_event(Event::CollectionLocked {305 issuer: sender,306 collection_id,307 });308309 Ok(())310 }311312 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]313 #[transactional]314 pub fn mint_nft(315 origin: OriginFor<T>,316 owner: T::AccountId,317 collection_id: RmrkCollectionId,318 recipient: Option<T::AccountId>,319 royalty_amount: Option<Permill>,320 metadata: RmrkString,321 transferable: bool,322 ) -> DispatchResult {323 let sender = ensure_signed(origin)?;324 let sender = T::CrossAccountId::from_sub(sender);325 let cross_owner = T::CrossAccountId::from_sub(owner.clone());326327 let collection = Self::get_typed_nft_collection(328 Self::unique_collection_id(collection_id)?,329 misc::CollectionType::Regular,330 )?;331 collection.check_is_external()?;332333 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {334 recipient: recipient.unwrap_or_else(|| owner.clone()),335 amount,336 });337338 let nft_id = Self::create_nft(339 &sender,340 &cross_owner,341 &collection,342 [343 Self::rmrk_property(TokenType, &NftType::Regular)?,344 Self::rmrk_property(Transferable, &transferable)?,345 Self::rmrk_property(PendingNftAccept, &false)?,346 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,347 Self::rmrk_property(Metadata, &metadata)?,348 Self::rmrk_property(Equipped, &false)?,349 Self::rmrk_property(350 ResourceCollection,351 &Self::init_collection(352 sender.clone(),353 CreateCollectionData {354 ..Default::default()355 },356 [Self::rmrk_property(357 CollectionType,358 &misc::CollectionType::Resource,359 )?]360 .into_iter(),361 )?,362 )?, // todo possibly add limits to the collection if rmrk warrants them363 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,364 ]365 .into_iter(),366 )367 .map_err(|err| match err {368 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),369 err => Self::map_unique_err_to_proxy(err),370 })?;371372 Self::deposit_event(Event::NftMinted {373 owner,374 collection_id,375 nft_id: nft_id.0,376 });377378 Ok(())379 }380381 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]382 #[transactional]383 pub fn burn_nft(384 origin: OriginFor<T>,385 collection_id: RmrkCollectionId,386 nft_id: RmrkNftId,387 ) -> DispatchResult {388 let sender = ensure_signed(origin)?;389 let cross_sender = T::CrossAccountId::from_sub(sender.clone());390391 let collection = Self::get_typed_nft_collection(392 Self::unique_collection_id(collection_id)?,393 misc::CollectionType::Regular,394 )?;395 collection.check_is_external()?;396397 Self::destroy_nft(398 cross_sender,399 Self::unique_collection_id(collection_id)?,400 nft_id.into(),401 )402 .map_err(Self::map_unique_err_to_proxy)?;403404 Self::deposit_event(Event::NFTBurned {405 owner: sender,406 nft_id,407 });408409 Ok(())410 }411412 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]413 #[transactional]414 pub fn send(415 origin: OriginFor<T>,416 rmrk_collection_id: RmrkCollectionId,417 rmrk_nft_id: RmrkNftId,418 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,419 ) -> DispatchResult {420 let sender = ensure_signed(origin.clone())?;421 let cross_sender = T::CrossAccountId::from_sub(sender.clone());422423 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;424 let nft_id = rmrk_nft_id.into();425426 let collection =427 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;428 collection.check_is_external()?;429430 let token_data =431 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;432433 let from = token_data.owner;434435 ensure!(436 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,437 <Error<T>>::NonTransferable438 );439440 ensure!(441 !Self::get_nft_property_decoded(442 collection_id,443 nft_id,444 RmrkProperty::PendingNftAccept445 )?,446 <Error<T>>::NoPermission447 );448449 let target_owner;450 let approval_required;451452 match new_owner {453 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {454 target_owner = T::CrossAccountId::from_sub(account_id.clone());455 approval_required = false;456 }457 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(458 target_collection_id,459 target_nft_id,460 ) => {461 let target_collection_id = Self::unique_collection_id(target_collection_id)?;462463 let target_nft_budget = budget::Value::new(NESTING_BUDGET);464465 let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(466 target_collection_id,467 target_nft_id.into(),468 Some((collection_id, nft_id)),469 &target_nft_budget,470 )471 .map_err(Self::map_unique_err_to_proxy)?;472473 approval_required = cross_sender != target_nft_owner;474475 if approval_required {476 target_owner = target_nft_owner;477478 <PalletNft<T>>::set_scoped_token_property(479 collection.id,480 nft_id,481 PropertyScope::Rmrk,482 Self::rmrk_property(PendingNftAccept, &approval_required)?,483 )?;484 } else {485 target_owner = T::CrossTokenAddressMapping::token_to_address(486 target_collection_id,487 target_nft_id.into(),488 );489 }490 }491 }492493 let src_nft_budget = budget::Value::new(NESTING_BUDGET);494495 <PalletNft<T>>::transfer_from(496 &collection,497 &cross_sender,498 &from,499 &target_owner,500 nft_id,501 &src_nft_budget,502 )503 .map_err(Self::map_unique_err_to_proxy)?;504505 Self::deposit_event(Event::NFTSent {506 sender,507 recipient: new_owner,508 collection_id: rmrk_collection_id,509 nft_id: rmrk_nft_id,510 approval_required,511 });512513 Ok(())514 }515516 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]517 #[transactional]518 pub fn accept_nft(519 origin: OriginFor<T>,520 rmrk_collection_id: RmrkCollectionId,521 rmrk_nft_id: RmrkNftId,522 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,523 ) -> DispatchResult {524 let sender = ensure_signed(origin.clone())?;525 let cross_sender = T::CrossAccountId::from_sub(sender.clone());526527 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;528 let nft_id = rmrk_nft_id.into();529530 let collection =531 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;532 collection.check_is_external()?;533534 let new_cross_owner = match new_owner {535 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {536 T::CrossAccountId::from_sub(account_id.clone())537 }538 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(539 target_collection_id,540 target_nft_id,541 ) => {542 let target_collection_id = Self::unique_collection_id(target_collection_id)?;543544 T::CrossTokenAddressMapping::token_to_address(545 target_collection_id,546 TokenId(target_nft_id),547 )548 }549 };550551 let budget = budget::Value::new(NESTING_BUDGET);552553 <PalletNft<T>>::transfer(554 &collection,555 &cross_sender,556 &new_cross_owner,557 nft_id,558 &budget,559 )560 .map_err(|err| {561 if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {562 <Error<T>>::CannotAcceptNonOwnedNft.into()563 } else {564 Self::map_unique_err_to_proxy(err)565 }566 })?;567568 <PalletNft<T>>::set_scoped_token_property(569 collection.id,570 nft_id,571 PropertyScope::Rmrk,572 Self::rmrk_property(PendingNftAccept, &false)?,573 )?;574575 Self::deposit_event(Event::NFTAccepted {576 sender,577 recipient: new_owner,578 collection_id: rmrk_collection_id,579 nft_id: rmrk_nft_id,580 });581582 Ok(())583 }584585 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]586 #[transactional]587 pub fn reject_nft(588 origin: OriginFor<T>,589 rmrk_collection_id: RmrkCollectionId,590 rmrk_nft_id: RmrkNftId,591 ) -> DispatchResult {592 let sender = ensure_signed(origin)?;593 let cross_sender = T::CrossAccountId::from_sub(sender.clone());594595 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;596 let nft_id = rmrk_nft_id.into();597598 let collection =599 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;600 collection.check_is_external()?;601602 Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {603 if err == <CommonError<T>>::NoPermission.into()604 || err == <CommonError<T>>::ApprovedValueTooLow.into()605 {606 <Error<T>>::CannotRejectNonOwnedNft.into()607 } else {608 Self::map_unique_err_to_proxy(err)609 }610 })?;611612 Self::deposit_event(Event::NFTRejected {613 sender,614 collection_id: rmrk_collection_id,615 nft_id: rmrk_nft_id,616 });617618 Ok(())619 }620621 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]622 #[transactional]623 pub fn accept_resource(624 origin: OriginFor<T>,625 rmrk_collection_id: RmrkCollectionId,626 rmrk_nft_id: RmrkNftId,627 rmrk_resource_id: RmrkResourceId,628 ) -> DispatchResult {629 let sender = ensure_signed(origin)?;630 let cross_sender = T::CrossAccountId::from_sub(sender);631632 let collection_id = Self::unique_collection_id(rmrk_collection_id)633 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;634 let collection =635 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;636 collection.check_is_external()?;637638 let nft_id = rmrk_nft_id.into();639 let resource_id = rmrk_resource_id.into();640641 let budget = budget::Value::new(NESTING_BUDGET);642643 let nft_owner =644 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)645 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;646647 let resource_collection_id: CollectionId =648 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)649 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;650651 let is_pending: bool = Self::get_nft_property_decoded(652 resource_collection_id,653 resource_id,654 PendingResourceAccept,655 )656 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;657658 ensure!(is_pending, <Error<T>>::ResourceNotPending);659660 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);661662 <PalletNft<T>>::set_scoped_token_property(663 resource_collection_id,664 rmrk_resource_id.into(),665 PropertyScope::Rmrk,666 Self::rmrk_property(PendingResourceAccept, &false)?,667 )?;668669 Self::deposit_event(Event::<T>::ResourceAccepted {670 nft_id: rmrk_nft_id,671 resource_id: rmrk_resource_id,672 });673674 Ok(())675 }676677 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]678 #[transactional]679 pub fn accept_resource_removal(680 origin: OriginFor<T>,681 rmrk_collection_id: RmrkCollectionId,682 rmrk_nft_id: RmrkNftId,683 rmrk_resource_id: RmrkResourceId,684 ) -> DispatchResult {685 let sender = ensure_signed(origin)?;686 let cross_sender = T::CrossAccountId::from_sub(sender);687688 let collection_id = Self::unique_collection_id(rmrk_collection_id)689 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;690 let collection =691 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;692 collection.check_is_external()?;693694 let nft_id = rmrk_nft_id.into();695 let resource_id = rmrk_resource_id.into();696697 let budget = budget::Value::new(NESTING_BUDGET);698699 let nft_owner =700 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)701 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;702703 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);704705 let resource_collection_id: CollectionId =706 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)707 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;708709 let is_pending: bool = Self::get_nft_property_decoded(710 resource_collection_id,711 resource_id,712 PendingResourceRemoval,713 )714 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;715716 ensure!(is_pending, <Error<T>>::ResourceNotPending);717718 let resource_collection = Self::get_typed_nft_collection(719 resource_collection_id,720 misc::CollectionType::Resource,721 )?;722723 <PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())724 .map_err(Self::map_unique_err_to_proxy)?;725726 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {727 nft_id: rmrk_nft_id,728 resource_id: rmrk_resource_id,729 });730731 Ok(())732 }733734 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]735 #[transactional]736 pub fn set_property(737 origin: OriginFor<T>,738 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,739 maybe_nft_id: Option<RmrkNftId>,740 key: RmrkKeyString,741 value: RmrkValueString,742 ) -> DispatchResult {743 let sender = ensure_signed(origin)?;744 let sender = T::CrossAccountId::from_sub(sender);745746 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;747 let collection =748 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;749 collection.check_is_external()?;750751 let budget = budget::Value::new(NESTING_BUDGET);752753 match maybe_nft_id {754 Some(nft_id) => {755 let token_id: TokenId = nft_id.into();756757 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;758 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;759760 <PalletNft<T>>::set_scoped_token_property(761 collection_id,762 token_id,763 PropertyScope::Rmrk,764 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,765 )?;766 }767 None => {768 let collection = Self::get_typed_nft_collection(769 collection_id,770 misc::CollectionType::Regular,771 )?;772773 Self::check_collection_owner(&collection, &sender)?;774775 <PalletCommon<T>>::set_scoped_collection_property(776 collection_id,777 PropertyScope::Rmrk,778 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,779 )?;780 }781 }782783 Self::deposit_event(Event::PropertySet {784 collection_id: rmrk_collection_id,785 maybe_nft_id,786 key,787 value,788 });789790 Ok(())791 }792793 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]794 #[transactional]795 pub fn set_priority(796 origin: OriginFor<T>,797 rmrk_collection_id: RmrkCollectionId,798 rmrk_nft_id: RmrkNftId,799 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,800 ) -> DispatchResult {801 let sender = ensure_signed(origin)?;802 let sender = T::CrossAccountId::from_sub(sender);803804 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;805 let nft_id = rmrk_nft_id.into();806807 let collection =808 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;809 collection.check_is_external()?;810811 let budget = budget::Value::new(NESTING_BUDGET);812813 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;814 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;815816 <PalletNft<T>>::set_scoped_token_property(817 collection_id,818 nft_id,819 PropertyScope::Rmrk,820 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,821 )?;822823 Self::deposit_event(Event::<T>::PrioritySet {824 collection_id: rmrk_collection_id,825 nft_id: rmrk_nft_id,826 });827828 Ok(())829 }830831 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]832 #[transactional]833 pub fn add_basic_resource(834 origin: OriginFor<T>,835 rmrk_collection_id: RmrkCollectionId,836 nft_id: RmrkNftId,837 resource: RmrkBasicResource,838 ) -> DispatchResult {839 let sender = ensure_signed(origin.clone())?;840841 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;842 let collection =843 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;844 collection.check_is_external()?;845846 let resource_id = Self::resource_add(847 sender,848 collection_id,849 nft_id.into(),850 [851 Self::rmrk_property(TokenType, &NftType::Resource)?,852 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,853 Self::rmrk_property(Src, &resource.src)?,854 Self::rmrk_property(Metadata, &resource.metadata)?,855 Self::rmrk_property(License, &resource.license)?,856 Self::rmrk_property(Thumb, &resource.thumb)?,857 ]858 .into_iter(),859 )?;860861 Self::deposit_event(Event::ResourceAdded {862 nft_id,863 resource_id,864 });865 Ok(())866 }867868 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]869 #[transactional]870 pub fn add_composable_resource(871 origin: OriginFor<T>,872 rmrk_collection_id: RmrkCollectionId,873 nft_id: RmrkNftId,874 _resource_id: RmrkBoundedResource,875 resource: RmrkComposableResource,876 ) -> DispatchResult {877 let sender = ensure_signed(origin.clone())?;878879 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;880 let collection =881 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;882 collection.check_is_external()?;883884 let resource_id = Self::resource_add(885 sender,886 collection_id,887 nft_id.into(),888 [889 Self::rmrk_property(TokenType, &NftType::Resource)?,890 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,891 Self::rmrk_property(Parts, &resource.parts)?,892 Self::rmrk_property(Base, &resource.base)?,893 Self::rmrk_property(Src, &resource.src)?,894 Self::rmrk_property(Metadata, &resource.metadata)?,895 Self::rmrk_property(License, &resource.license)?,896 Self::rmrk_property(Thumb, &resource.thumb)?,897 ]898 .into_iter(),899 )?;900901 Self::deposit_event(Event::ResourceAdded {902 nft_id,903 resource_id,904 });905 Ok(())906 }907908 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]909 #[transactional]910 pub fn add_slot_resource(911 origin: OriginFor<T>,912 rmrk_collection_id: RmrkCollectionId,913 nft_id: RmrkNftId,914 resource: RmrkSlotResource,915 ) -> DispatchResult {916 let sender = ensure_signed(origin.clone())?;917918 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;919 let collection =920 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;921 collection.check_is_external()?;922923 let resource_id = Self::resource_add(924 sender,925 collection_id,926 nft_id.into(),927 [928 Self::rmrk_property(TokenType, &NftType::Resource)?,929 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,930 Self::rmrk_property(Base, &resource.base)?,931 Self::rmrk_property(Src, &resource.src)?,932 Self::rmrk_property(Metadata, &resource.metadata)?,933 Self::rmrk_property(Slot, &resource.slot)?,934 Self::rmrk_property(License, &resource.license)?,935 Self::rmrk_property(Thumb, &resource.thumb)?,936 ]937 .into_iter(),938 )?;939940 Self::deposit_event(Event::ResourceAdded {941 nft_id,942 resource_id,943 });944 Ok(())945 }946947 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]948 #[transactional]949 pub fn remove_resource(950 origin: OriginFor<T>,951 rmrk_collection_id: RmrkCollectionId,952 nft_id: RmrkNftId,953 resource_id: RmrkResourceId,954 ) -> DispatchResult {955 let sender = ensure_signed(origin.clone())?;956957 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;958 let collection =959 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;960 collection.check_is_external()?;961962 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;963964 Self::deposit_event(Event::ResourceRemoval {965 nft_id,966 resource_id,967 });968 Ok(())969 }970 }971}972973impl<T: Config> Pallet<T> {974 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {975 let key = rmrk_key.to_key::<T>()?;976977 let scoped_key = PropertyScope::Rmrk978 .apply(key)979 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;980981 Ok(scoped_key)982 }983984 // todo think about renaming these985 pub fn rmrk_property<E: Encode>(986 rmrk_key: RmrkProperty,987 value: &E,988 ) -> Result<Property, DispatchError> {989 let key = rmrk_key.to_key::<T>()?;990991 let value = value992 .encode()993 .try_into()994 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;995996 let property = Property { key, value };997998 Ok(property)999 }10001001 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1002 vec.decode()1003 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1004 }10051006 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1007 where1008 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1009 {1010 vec.rebind()1011 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1012 }10131014 fn init_collection(1015 sender: T::CrossAccountId,1016 data: CreateCollectionData<T::AccountId>,1017 properties: impl Iterator<Item = Property>,1018 ) -> Result<CollectionId, DispatchError> {1019 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10201021 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1022 return Err(<Error<T>>::NoAvailableCollectionId.into());1023 }10241025 <PalletCommon<T>>::set_scoped_collection_properties(1026 collection_id?,1027 PropertyScope::Rmrk,1028 properties,1029 )?;10301031 collection_id1032 }10331034 pub fn create_nft(1035 sender: &T::CrossAccountId,1036 owner: &T::CrossAccountId,1037 collection: &NonfungibleHandle<T>,1038 properties: impl Iterator<Item = Property>,1039 ) -> Result<TokenId, DispatchError> {1040 let data = CreateNftExData {1041 properties: BoundedVec::default(),1042 owner: owner.clone(),1043 };10441045 let budget = budget::Value::new(NESTING_BUDGET);10461047 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10481049 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10501051 <PalletNft<T>>::set_scoped_token_properties(1052 collection.id,1053 nft_id,1054 PropertyScope::Rmrk,1055 properties,1056 )?;10571058 Ok(nft_id)1059 }10601061 fn destroy_nft(1062 sender: T::CrossAccountId,1063 collection_id: CollectionId,1064 token_id: TokenId,1065 ) -> DispatchResult {1066 let collection =1067 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10681069 let token_data =1070 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10711072 let from = token_data.owner;10731074 let budget = budget::Value::new(NESTING_BUDGET);10751076 <PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1077 }10781079 fn resource_add(1080 sender: T::AccountId,1081 collection_id: CollectionId,1082 token_id: TokenId,1083 resource_properties: impl Iterator<Item = Property>,1084 ) -> Result<RmrkResourceId, DispatchError> {1085 let collection =1086 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1087 ensure!(collection.owner == sender, Error::<T>::NoPermission);10881089 let sender = T::CrossAccountId::from_sub(sender);1090 let budget = budget::Value::new(NESTING_BUDGET);10911092 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1093 .map_err(Self::map_unique_err_to_proxy)?;10941095 let pending = sender != nft_owner;10961097 let resource_collection_id: CollectionId =1098 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1099 let resource_collection =1100 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11011102 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11031104 let resource_id = Self::create_nft(1105 &sender,1106 &nft_owner,1107 &resource_collection,1108 resource_properties.chain(1109 [1110 Self::rmrk_property(PendingResourceAccept, &pending)?,1111 Self::rmrk_property(PendingResourceRemoval, &false)?,1112 ]1113 .into_iter(),1114 ),1115 )1116 .map_err(|err| match err {1117 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1118 err => Self::map_unique_err_to_proxy(err),1119 })?;11201121 Ok(resource_id.0)1122 }11231124 fn resource_remove(1125 sender: T::AccountId,1126 collection_id: CollectionId,1127 nft_id: TokenId,1128 resource_id: TokenId,1129 ) -> DispatchResult {1130 let collection =1131 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1132 ensure!(collection.owner == sender, Error::<T>::NoPermission);11331134 let resource_collection_id: CollectionId =1135 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1136 let resource_collection =1137 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1138 ensure!(1139 <PalletNft<T>>::token_exists(&resource_collection, resource_id),1140 Error::<T>::ResourceDoesntExist1141 );11421143 let budget = up_data_structs::budget::Value::new(10);1144 let topmost_owner =1145 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;11461147 let sender = T::CrossAccountId::from_sub(sender);1148 if topmost_owner == sender {1149 <PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1150 .map_err(Self::map_unique_err_to_proxy)?;1151 } else {1152 <PalletNft<T>>::set_scoped_token_property(1153 resource_collection_id,1154 resource_id,1155 PropertyScope::Rmrk,1156 Self::rmrk_property(PendingResourceRemoval, &true)?,1157 )?;1158 }11591160 Ok(())1161 }11621163 fn change_collection_owner(1164 collection_id: CollectionId,1165 collection_type: misc::CollectionType,1166 sender: T::AccountId,1167 new_owner: T::AccountId,1168 ) -> DispatchResult {1169 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1170 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11711172 let mut collection = collection.into_inner();11731174 collection.owner = new_owner;1175 collection.save()1176 }11771178 fn check_collection_owner(1179 collection: &NonfungibleHandle<T>,1180 account: &T::CrossAccountId,1181 ) -> DispatchResult {1182 collection1183 .check_is_owner(account)1184 .map_err(Self::map_unique_err_to_proxy)1185 }11861187 pub fn last_collection_idx() -> RmrkCollectionId {1188 <CollectionIndex<T>>::get()1189 }11901191 pub fn unique_collection_id(1192 rmrk_collection_id: RmrkCollectionId,1193 ) -> Result<CollectionId, DispatchError> {1194 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1195 .map_err(|_| <Error<T>>::CollectionUnknown.into())1196 }11971198 pub fn rmrk_collection_id(1199 unique_collection_id: CollectionId,1200 ) -> Result<RmrkCollectionId, DispatchError> {1201 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1202 .map_err(|_| <Error<T>>::CollectionUnknown.into())1203 }12041205 pub fn get_nft_collection(1206 collection_id: CollectionId,1207 ) -> Result<NonfungibleHandle<T>, DispatchError> {1208 let collection = <CollectionHandle<T>>::try_get(collection_id)1209 .map_err(|_| <Error<T>>::CollectionUnknown)?;12101211 match collection.mode {1212 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1213 _ => Err(<Error<T>>::CollectionUnknown.into()),1214 }1215 }12161217 pub fn collection_exists(collection_id: CollectionId) -> bool {1218 <CollectionHandle<T>>::try_get(collection_id).is_ok()1219 }12201221 pub fn get_collection_property(1222 collection_id: CollectionId,1223 key: RmrkProperty,1224 ) -> Result<PropertyValue, DispatchError> {1225 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1226 .get(&Self::rmrk_property_key(key)?)1227 .ok_or(<Error<T>>::CollectionUnknown)?1228 .clone();12291230 Ok(collection_property)1231 }12321233 pub fn get_collection_property_decoded<V: Decode>(1234 collection_id: CollectionId,1235 key: RmrkProperty,1236 ) -> Result<V, DispatchError> {1237 Self::decode_property(Self::get_collection_property(collection_id, key)?)1238 }12391240 pub fn get_collection_type(1241 collection_id: CollectionId,1242 ) -> Result<misc::CollectionType, DispatchError> {1243 Self::get_collection_property_decoded(collection_id, CollectionType)1244 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1245 }12461247 pub fn ensure_collection_type(1248 collection_id: CollectionId,1249 collection_type: misc::CollectionType,1250 ) -> DispatchResult {1251 let actual_type = Self::get_collection_type(collection_id)?;1252 ensure!(1253 actual_type == collection_type,1254 <CommonError<T>>::NoPermission1255 );12561257 Ok(())1258 }12591260 pub fn get_typed_nft_collection(1261 collection_id: CollectionId,1262 collection_type: misc::CollectionType,1263 ) -> Result<NonfungibleHandle<T>, DispatchError> {1264 Self::ensure_collection_type(collection_id, collection_type)?;12651266 Self::get_nft_collection(collection_id)1267 }12681269 pub fn get_typed_nft_collection_mapped(1270 rmrk_collection_id: RmrkCollectionId,1271 collection_type: misc::CollectionType,1272 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1273 let unique_collection_id = match collection_type {1274 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1275 _ => rmrk_collection_id.into(),1276 };12771278 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12791280 Ok((collection, unique_collection_id))1281 }12821283 pub fn get_nft_property(1284 collection_id: CollectionId,1285 nft_id: TokenId,1286 key: RmrkProperty,1287 ) -> Result<PropertyValue, DispatchError> {1288 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1289 .get(&Self::rmrk_property_key(key)?)1290 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1291 .clone();12921293 Ok(nft_property)1294 }12951296 pub fn get_nft_property_decoded<V: Decode>(1297 collection_id: CollectionId,1298 nft_id: TokenId,1299 key: RmrkProperty,1300 ) -> Result<V, DispatchError> {1301 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1302 }13031304 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1305 <TokenData<T>>::contains_key((collection_id, nft_id))1306 }13071308 pub fn get_nft_type(1309 collection_id: CollectionId,1310 token_id: TokenId,1311 ) -> Result<NftType, DispatchError> {1312 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1313 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1314 }13151316 pub fn ensure_nft_type(1317 collection_id: CollectionId,1318 token_id: TokenId,1319 nft_type: NftType,1320 ) -> DispatchResult {1321 let actual_type = Self::get_nft_type(collection_id, token_id)?;1322 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13231324 Ok(())1325 }13261327 pub fn ensure_nft_owner(1328 collection_id: CollectionId,1329 token_id: TokenId,1330 possible_owner: &T::CrossAccountId,1331 nesting_budget: &dyn budget::Budget,1332 ) -> DispatchResult {1333 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1334 possible_owner.clone(),1335 collection_id,1336 token_id,1337 None,1338 nesting_budget,1339 )1340 .map_err(Self::map_unique_err_to_proxy)?;13411342 ensure!(is_owned, <Error<T>>::NoPermission);13431344 Ok(())1345 }13461347 pub fn filter_user_properties<Key, Value, R, Mapper>(1348 collection_id: CollectionId,1349 token_id: Option<TokenId>,1350 filter_keys: Option<Vec<RmrkPropertyKey>>,1351 mapper: Mapper,1352 ) -> Result<Vec<R>, DispatchError>1353 where1354 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1355 Value: Decode + Default,1356 Mapper: Fn(Key, Value) -> R,1357 {1358 filter_keys1359 .map(|keys| {1360 let properties = keys1361 .into_iter()1362 .filter_map(|key| {1363 let key: Key = key.try_into().ok()?;13641365 let value = match token_id {1366 Some(token_id) => Self::get_nft_property_decoded(1367 collection_id,1368 token_id,1369 UserProperty(key.as_ref()),1370 ),1371 None => Self::get_collection_property_decoded(1372 collection_id,1373 UserProperty(key.as_ref()),1374 ),1375 }1376 .ok()?;13771378 Some(mapper(key, value))1379 })1380 .collect();13811382 Ok(properties)1383 })1384 .unwrap_or_else(|| {1385 let properties =1386 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13871388 Ok(properties)1389 })1390 }13911392 pub fn iterate_user_properties<Key, Value, R, Mapper>(1393 collection_id: CollectionId,1394 token_id: Option<TokenId>,1395 mapper: Mapper,1396 ) -> Result<impl Iterator<Item = R>, DispatchError>1397 where1398 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1399 Value: Decode + Default,1400 Mapper: Fn(Key, Value) -> R,1401 {1402 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14031404 let properties = match token_id {1405 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1406 None => <PalletCommon<T>>::collection_properties(collection_id),1407 };14081409 let properties = properties.into_iter().filter_map(move |(key, value)| {1410 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14111412 let key: Key = key.to_vec().try_into().ok()?;1413 let value: Value = value.decode().ok()?;14141415 Some(mapper(key, value))1416 });14171418 Ok(properties)1419 }14201421 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1422 map_unique_err_to_proxy! {1423 match err {1424 CommonError::NoPermission => NoPermission,1425 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1426 CommonError::PublicMintingNotAllowed => NoPermission,1427 CommonError::TokenNotFound => NoAvailableNftId,1428 CommonError::ApprovedValueTooLow => NoPermission,1429 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1430 StructureError::TokenNotFound => NoAvailableNftId,1431 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1432 }1433 }1434 }1435}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 weights;3940pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4142use weights::WeightInfo;43use misc::*;44pub use property::*;4546use RmrkProperty::*;4748const 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::storage]72 pub type RmrkInernalCollectionId<T: Config> =73 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;7475 #[pallet::pallet]76 #[pallet::generate_store(pub(super) trait Store)]77 pub struct Pallet<T>(_);7879 #[pallet::event]80 #[pallet::generate_deposit(pub(super) fn deposit_event)]81 pub enum Event<T: Config> {82 CollectionCreated {83 issuer: T::AccountId,84 collection_id: RmrkCollectionId,85 },86 CollectionDestroyed {87 issuer: T::AccountId,88 collection_id: RmrkCollectionId,89 },90 IssuerChanged {91 old_issuer: T::AccountId,92 new_issuer: T::AccountId,93 collection_id: RmrkCollectionId,94 },95 CollectionLocked {96 issuer: T::AccountId,97 collection_id: RmrkCollectionId,98 },99 NftMinted {100 owner: T::AccountId,101 collection_id: RmrkCollectionId,102 nft_id: RmrkNftId,103 },104 NFTBurned {105 owner: T::AccountId,106 nft_id: RmrkNftId,107 },108 NFTSent {109 sender: T::AccountId,110 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111 collection_id: RmrkCollectionId,112 nft_id: RmrkNftId,113 approval_required: bool,114 },115 NFTAccepted {116 sender: T::AccountId,117 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,118 collection_id: RmrkCollectionId,119 nft_id: RmrkNftId,120 },121 NFTRejected {122 sender: T::AccountId,123 collection_id: RmrkCollectionId,124 nft_id: RmrkNftId,125 },126 PropertySet {127 collection_id: RmrkCollectionId,128 maybe_nft_id: Option<RmrkNftId>,129 key: RmrkKeyString,130 value: RmrkValueString,131 },132 ResourceAdded {133 nft_id: RmrkNftId,134 resource_id: RmrkResourceId,135 },136 ResourceRemoval {137 nft_id: RmrkNftId,138 resource_id: RmrkResourceId,139 },140 ResourceAccepted {141 nft_id: RmrkNftId,142 resource_id: RmrkResourceId,143 },144 ResourceRemovalAccepted {145 nft_id: RmrkNftId,146 resource_id: RmrkResourceId,147 },148 PrioritySet {149 collection_id: RmrkCollectionId,150 nft_id: RmrkNftId,151 },152 }153154 #[pallet::error]155 pub enum Error<T> {156 /* Unique-specific events */157 CorruptedCollectionType,158 NftTypeEncodeError,159 RmrkPropertyKeyIsTooLong,160 RmrkPropertyValueIsTooLong,161162 /* RMRK compatible events */163 CollectionNotEmpty,164 NoAvailableCollectionId,165 NoAvailableNftId,166 CollectionUnknown,167 NoPermission,168 NonTransferable,169 CollectionFullOrLocked,170 ResourceDoesntExist,171 CannotSendToDescendentOrSelf,172 CannotAcceptNonOwnedNft,173 CannotRejectNonOwnedNft,174 ResourceNotPending,175 }176177 #[pallet::call]178 impl<T: Config> Pallet<T> {179 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]180 #[transactional]181 pub fn create_collection(182 origin: OriginFor<T>,183 metadata: RmrkString,184 max: Option<u32>,185 symbol: RmrkCollectionSymbol,186 ) -> DispatchResult {187 let sender = ensure_signed(origin)?;188189 let limits = CollectionLimits {190 owner_can_transfer: Some(false),191 token_limit: max,192 ..Default::default()193 };194195 let data = CreateCollectionData {196 limits: Some(limits),197 token_prefix: symbol198 .into_inner()199 .try_into()200 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,201 permissions: Some(CollectionPermissions {202 nesting: Some(NestingRule::Owner),203 ..Default::default()204 }),205 ..Default::default()206 };207208 let unique_collection_id = Self::init_collection(209 T::CrossAccountId::from_sub(sender.clone()),210 data,211 [212 Self::rmrk_property(Metadata, &metadata)?,213 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,214 ]215 .into_iter(),216 )?;217 let rmrk_collection_id = <CollectionIndex<T>>::get();218219 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);220 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);221222 <CollectionIndex<T>>::mutate(|n| *n += 1);223224 Self::deposit_event(Event::CollectionCreated {225 issuer: sender,226 collection_id: rmrk_collection_id,227 });228229 Ok(())230 }231232 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]233 #[transactional]234 pub fn destroy_collection(235 origin: OriginFor<T>,236 collection_id: RmrkCollectionId,237 ) -> DispatchResult {238 let sender = ensure_signed(origin)?;239 let cross_sender = T::CrossAccountId::from_sub(sender.clone());240241 let collection = Self::get_typed_nft_collection(242 Self::unique_collection_id(collection_id)?,243 misc::CollectionType::Regular,244 )?;245 collection.check_is_external()?;246247 <PalletNft<T>>::destroy_collection(collection, &cross_sender)248 .map_err(Self::map_unique_err_to_proxy)?;249250 Self::deposit_event(Event::CollectionDestroyed {251 issuer: sender,252 collection_id,253 });254255 Ok(())256 }257258 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]259 #[transactional]260 pub fn change_collection_issuer(261 origin: OriginFor<T>,262 collection_id: RmrkCollectionId,263 new_issuer: <T::Lookup as StaticLookup>::Source,264 ) -> DispatchResult {265 let sender = ensure_signed(origin)?;266267 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;268 collection.check_is_external()?;269270 let new_issuer = T::Lookup::lookup(new_issuer)?;271272 Self::change_collection_owner(273 Self::unique_collection_id(collection_id)?,274 misc::CollectionType::Regular,275 sender.clone(),276 new_issuer.clone(),277 )?;278279 Self::deposit_event(Event::IssuerChanged {280 old_issuer: sender,281 new_issuer,282 collection_id,283 });284285 Ok(())286 }287288 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]289 #[transactional]290 pub fn lock_collection(291 origin: OriginFor<T>,292 collection_id: RmrkCollectionId,293 ) -> DispatchResult {294 let sender = ensure_signed(origin)?;295 let cross_sender = T::CrossAccountId::from_sub(sender.clone());296297 let collection = Self::get_typed_nft_collection(298 Self::unique_collection_id(collection_id)?,299 misc::CollectionType::Regular,300 )?;301 collection.check_is_external()?;302303 Self::check_collection_owner(&collection, &cross_sender)?;304305 let token_count = collection.total_supply();306307 let mut collection = collection.into_inner();308 collection.limits.token_limit = Some(token_count);309 collection.save()?;310311 Self::deposit_event(Event::CollectionLocked {312 issuer: sender,313 collection_id,314 });315316 Ok(())317 }318319 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]320 #[transactional]321 pub fn mint_nft(322 origin: OriginFor<T>,323 owner: T::AccountId,324 collection_id: RmrkCollectionId,325 recipient: Option<T::AccountId>,326 royalty_amount: Option<Permill>,327 metadata: RmrkString,328 transferable: bool,329 ) -> DispatchResult {330 let sender = ensure_signed(origin)?;331 let sender = T::CrossAccountId::from_sub(sender);332 let cross_owner = T::CrossAccountId::from_sub(owner.clone());333334 let collection = Self::get_typed_nft_collection(335 Self::unique_collection_id(collection_id)?,336 misc::CollectionType::Regular,337 )?;338 collection.check_is_external()?;339340 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {341 recipient: recipient.unwrap_or_else(|| owner.clone()),342 amount,343 });344345 let nft_id = Self::create_nft(346 &sender,347 &cross_owner,348 &collection,349 [350 Self::rmrk_property(TokenType, &NftType::Regular)?,351 Self::rmrk_property(Transferable, &transferable)?,352 Self::rmrk_property(PendingNftAccept, &false)?,353 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,354 Self::rmrk_property(Metadata, &metadata)?,355 Self::rmrk_property(Equipped, &false)?,356 Self::rmrk_property(357 ResourceCollection,358 &Self::init_collection(359 sender.clone(),360 CreateCollectionData {361 ..Default::default()362 },363 [Self::rmrk_property(364 CollectionType,365 &misc::CollectionType::Resource,366 )?]367 .into_iter(),368 )?,369 )?, // todo possibly add limits to the collection if rmrk warrants them370 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,371 ]372 .into_iter(),373 )374 .map_err(|err| match err {375 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),376 err => Self::map_unique_err_to_proxy(err),377 })?;378379 Self::deposit_event(Event::NftMinted {380 owner,381 collection_id,382 nft_id: nft_id.0,383 });384385 Ok(())386 }387388 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]389 #[transactional]390 pub fn burn_nft(391 origin: OriginFor<T>,392 collection_id: RmrkCollectionId,393 nft_id: RmrkNftId,394 ) -> DispatchResult {395 let sender = ensure_signed(origin)?;396 let cross_sender = T::CrossAccountId::from_sub(sender.clone());397398 let collection = Self::get_typed_nft_collection(399 Self::unique_collection_id(collection_id)?,400 misc::CollectionType::Regular,401 )?;402 collection.check_is_external()?;403404 Self::destroy_nft(405 cross_sender,406 Self::unique_collection_id(collection_id)?,407 nft_id.into(),408 )409 .map_err(Self::map_unique_err_to_proxy)?;410411 Self::deposit_event(Event::NFTBurned {412 owner: sender,413 nft_id,414 });415416 Ok(())417 }418419 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]420 #[transactional]421 pub fn send(422 origin: OriginFor<T>,423 rmrk_collection_id: RmrkCollectionId,424 rmrk_nft_id: RmrkNftId,425 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,426 ) -> DispatchResult {427 let sender = ensure_signed(origin.clone())?;428 let cross_sender = T::CrossAccountId::from_sub(sender.clone());429430 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;431 let nft_id = rmrk_nft_id.into();432433 let collection =434 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;435 collection.check_is_external()?;436437 let token_data =438 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;439440 let from = token_data.owner;441442 ensure!(443 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,444 <Error<T>>::NonTransferable445 );446447 ensure!(448 !Self::get_nft_property_decoded(449 collection_id,450 nft_id,451 RmrkProperty::PendingNftAccept452 )?,453 <Error<T>>::NoPermission454 );455456 let target_owner;457 let approval_required;458459 match new_owner {460 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {461 target_owner = T::CrossAccountId::from_sub(account_id.clone());462 approval_required = false;463 }464 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(465 target_collection_id,466 target_nft_id,467 ) => {468 let target_collection_id = Self::unique_collection_id(target_collection_id)?;469470 let target_nft_budget = budget::Value::new(NESTING_BUDGET);471472 let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(473 target_collection_id,474 target_nft_id.into(),475 Some((collection_id, nft_id)),476 &target_nft_budget,477 )478 .map_err(Self::map_unique_err_to_proxy)?;479480 approval_required = cross_sender != target_nft_owner;481482 if approval_required {483 target_owner = target_nft_owner;484485 <PalletNft<T>>::set_scoped_token_property(486 collection.id,487 nft_id,488 PropertyScope::Rmrk,489 Self::rmrk_property(PendingNftAccept, &approval_required)?,490 )?;491 } else {492 target_owner = T::CrossTokenAddressMapping::token_to_address(493 target_collection_id,494 target_nft_id.into(),495 );496 }497 }498 }499500 let src_nft_budget = budget::Value::new(NESTING_BUDGET);501502 <PalletNft<T>>::transfer_from(503 &collection,504 &cross_sender,505 &from,506 &target_owner,507 nft_id,508 &src_nft_budget,509 )510 .map_err(Self::map_unique_err_to_proxy)?;511512 Self::deposit_event(Event::NFTSent {513 sender,514 recipient: new_owner,515 collection_id: rmrk_collection_id,516 nft_id: rmrk_nft_id,517 approval_required,518 });519520 Ok(())521 }522523 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]524 #[transactional]525 pub fn accept_nft(526 origin: OriginFor<T>,527 rmrk_collection_id: RmrkCollectionId,528 rmrk_nft_id: RmrkNftId,529 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,530 ) -> DispatchResult {531 let sender = ensure_signed(origin.clone())?;532 let cross_sender = T::CrossAccountId::from_sub(sender.clone());533534 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;535 let nft_id = rmrk_nft_id.into();536537 let collection =538 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;539 collection.check_is_external()?;540541 let new_cross_owner = match new_owner {542 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {543 T::CrossAccountId::from_sub(account_id.clone())544 }545 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(546 target_collection_id,547 target_nft_id,548 ) => {549 let target_collection_id = Self::unique_collection_id(target_collection_id)?;550551 T::CrossTokenAddressMapping::token_to_address(552 target_collection_id,553 TokenId(target_nft_id),554 )555 }556 };557558 let budget = budget::Value::new(NESTING_BUDGET);559560 <PalletNft<T>>::transfer(561 &collection,562 &cross_sender,563 &new_cross_owner,564 nft_id,565 &budget,566 )567 .map_err(|err| {568 if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {569 <Error<T>>::CannotAcceptNonOwnedNft.into()570 } else {571 Self::map_unique_err_to_proxy(err)572 }573 })?;574575 <PalletNft<T>>::set_scoped_token_property(576 collection.id,577 nft_id,578 PropertyScope::Rmrk,579 Self::rmrk_property(PendingNftAccept, &false)?,580 )?;581582 Self::deposit_event(Event::NFTAccepted {583 sender,584 recipient: new_owner,585 collection_id: rmrk_collection_id,586 nft_id: rmrk_nft_id,587 });588589 Ok(())590 }591592 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]593 #[transactional]594 pub fn reject_nft(595 origin: OriginFor<T>,596 rmrk_collection_id: RmrkCollectionId,597 rmrk_nft_id: RmrkNftId,598 ) -> DispatchResult {599 let sender = ensure_signed(origin)?;600 let cross_sender = T::CrossAccountId::from_sub(sender.clone());601602 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;603 let nft_id = rmrk_nft_id.into();604605 let collection =606 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;607 collection.check_is_external()?;608609 Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {610 if err == <CommonError<T>>::NoPermission.into()611 || err == <CommonError<T>>::ApprovedValueTooLow.into()612 {613 <Error<T>>::CannotRejectNonOwnedNft.into()614 } else {615 Self::map_unique_err_to_proxy(err)616 }617 })?;618619 Self::deposit_event(Event::NFTRejected {620 sender,621 collection_id: rmrk_collection_id,622 nft_id: rmrk_nft_id,623 });624625 Ok(())626 }627628 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]629 #[transactional]630 pub fn accept_resource(631 origin: OriginFor<T>,632 rmrk_collection_id: RmrkCollectionId,633 rmrk_nft_id: RmrkNftId,634 rmrk_resource_id: RmrkResourceId,635 ) -> DispatchResult {636 let sender = ensure_signed(origin)?;637 let cross_sender = T::CrossAccountId::from_sub(sender);638639 let collection_id = Self::unique_collection_id(rmrk_collection_id)640 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;641 let collection =642 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;643 collection.check_is_external()?;644645 let nft_id = rmrk_nft_id.into();646 let resource_id = rmrk_resource_id.into();647648 let budget = budget::Value::new(NESTING_BUDGET);649650 let nft_owner =651 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)652 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;653654 let resource_collection_id: CollectionId =655 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)656 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;657658 let is_pending: bool = Self::get_nft_property_decoded(659 resource_collection_id,660 resource_id,661 PendingResourceAccept,662 )663 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;664665 ensure!(is_pending, <Error<T>>::ResourceNotPending);666667 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);668669 <PalletNft<T>>::set_scoped_token_property(670 resource_collection_id,671 rmrk_resource_id.into(),672 PropertyScope::Rmrk,673 Self::rmrk_property(PendingResourceAccept, &false)?,674 )?;675676 Self::deposit_event(Event::<T>::ResourceAccepted {677 nft_id: rmrk_nft_id,678 resource_id: rmrk_resource_id,679 });680681 Ok(())682 }683684 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]685 #[transactional]686 pub fn accept_resource_removal(687 origin: OriginFor<T>,688 rmrk_collection_id: RmrkCollectionId,689 rmrk_nft_id: RmrkNftId,690 rmrk_resource_id: RmrkResourceId,691 ) -> DispatchResult {692 let sender = ensure_signed(origin)?;693 let cross_sender = T::CrossAccountId::from_sub(sender);694695 let collection_id = Self::unique_collection_id(rmrk_collection_id)696 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;697 let collection =698 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;699 collection.check_is_external()?;700701 let nft_id = rmrk_nft_id.into();702 let resource_id = rmrk_resource_id.into();703704 let budget = budget::Value::new(NESTING_BUDGET);705706 let nft_owner =707 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)708 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;709710 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);711712 let resource_collection_id: CollectionId =713 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)714 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;715716 let is_pending: bool = Self::get_nft_property_decoded(717 resource_collection_id,718 resource_id,719 PendingResourceRemoval,720 )721 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;722723 ensure!(is_pending, <Error<T>>::ResourceNotPending);724725 let resource_collection = Self::get_typed_nft_collection(726 resource_collection_id,727 misc::CollectionType::Resource,728 )?;729730 <PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())731 .map_err(Self::map_unique_err_to_proxy)?;732733 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {734 nft_id: rmrk_nft_id,735 resource_id: rmrk_resource_id,736 });737738 Ok(())739 }740741 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]742 #[transactional]743 pub fn set_property(744 origin: OriginFor<T>,745 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,746 maybe_nft_id: Option<RmrkNftId>,747 key: RmrkKeyString,748 value: RmrkValueString,749 ) -> DispatchResult {750 let sender = ensure_signed(origin)?;751 let sender = T::CrossAccountId::from_sub(sender);752753 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;754 let collection =755 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;756 collection.check_is_external()?;757758 let budget = budget::Value::new(NESTING_BUDGET);759760 match maybe_nft_id {761 Some(nft_id) => {762 let token_id: TokenId = nft_id.into();763764 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;765 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;766767 <PalletNft<T>>::set_scoped_token_property(768 collection_id,769 token_id,770 PropertyScope::Rmrk,771 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,772 )?;773 }774 None => {775 let collection = Self::get_typed_nft_collection(776 collection_id,777 misc::CollectionType::Regular,778 )?;779780 Self::check_collection_owner(&collection, &sender)?;781782 <PalletCommon<T>>::set_scoped_collection_property(783 collection_id,784 PropertyScope::Rmrk,785 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,786 )?;787 }788 }789790 Self::deposit_event(Event::PropertySet {791 collection_id: rmrk_collection_id,792 maybe_nft_id,793 key,794 value,795 });796797 Ok(())798 }799800 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]801 #[transactional]802 pub fn set_priority(803 origin: OriginFor<T>,804 rmrk_collection_id: RmrkCollectionId,805 rmrk_nft_id: RmrkNftId,806 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,807 ) -> DispatchResult {808 let sender = ensure_signed(origin)?;809 let sender = T::CrossAccountId::from_sub(sender);810811 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;812 let nft_id = rmrk_nft_id.into();813814 let collection =815 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;816 collection.check_is_external()?;817818 let budget = budget::Value::new(NESTING_BUDGET);819820 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;821 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;822823 <PalletNft<T>>::set_scoped_token_property(824 collection_id,825 nft_id,826 PropertyScope::Rmrk,827 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,828 )?;829830 Self::deposit_event(Event::<T>::PrioritySet {831 collection_id: rmrk_collection_id,832 nft_id: rmrk_nft_id,833 });834835 Ok(())836 }837838 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]839 #[transactional]840 pub fn add_basic_resource(841 origin: OriginFor<T>,842 rmrk_collection_id: RmrkCollectionId,843 nft_id: RmrkNftId,844 resource: RmrkBasicResource,845 ) -> DispatchResult {846 let sender = ensure_signed(origin.clone())?;847848 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;849 let collection =850 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;851 collection.check_is_external()?;852853 let resource_id = Self::resource_add(854 sender,855 collection_id,856 nft_id.into(),857 [858 Self::rmrk_property(TokenType, &NftType::Resource)?,859 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,860 Self::rmrk_property(Src, &resource.src)?,861 Self::rmrk_property(Metadata, &resource.metadata)?,862 Self::rmrk_property(License, &resource.license)?,863 Self::rmrk_property(Thumb, &resource.thumb)?,864 ]865 .into_iter(),866 )?;867868 Self::deposit_event(Event::ResourceAdded {869 nft_id,870 resource_id,871 });872 Ok(())873 }874875 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]876 #[transactional]877 pub fn add_composable_resource(878 origin: OriginFor<T>,879 rmrk_collection_id: RmrkCollectionId,880 nft_id: RmrkNftId,881 _resource_id: RmrkBoundedResource,882 resource: RmrkComposableResource,883 ) -> DispatchResult {884 let sender = ensure_signed(origin.clone())?;885886 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;887 let collection =888 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;889 collection.check_is_external()?;890891 let resource_id = Self::resource_add(892 sender,893 collection_id,894 nft_id.into(),895 [896 Self::rmrk_property(TokenType, &NftType::Resource)?,897 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,898 Self::rmrk_property(Parts, &resource.parts)?,899 Self::rmrk_property(Base, &resource.base)?,900 Self::rmrk_property(Src, &resource.src)?,901 Self::rmrk_property(Metadata, &resource.metadata)?,902 Self::rmrk_property(License, &resource.license)?,903 Self::rmrk_property(Thumb, &resource.thumb)?,904 ]905 .into_iter(),906 )?;907908 Self::deposit_event(Event::ResourceAdded {909 nft_id,910 resource_id,911 });912 Ok(())913 }914915 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]916 #[transactional]917 pub fn add_slot_resource(918 origin: OriginFor<T>,919 rmrk_collection_id: RmrkCollectionId,920 nft_id: RmrkNftId,921 resource: RmrkSlotResource,922 ) -> DispatchResult {923 let sender = ensure_signed(origin.clone())?;924925 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;926 let collection =927 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;928 collection.check_is_external()?;929930 let resource_id = Self::resource_add(931 sender,932 collection_id,933 nft_id.into(),934 [935 Self::rmrk_property(TokenType, &NftType::Resource)?,936 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,937 Self::rmrk_property(Base, &resource.base)?,938 Self::rmrk_property(Src, &resource.src)?,939 Self::rmrk_property(Metadata, &resource.metadata)?,940 Self::rmrk_property(Slot, &resource.slot)?,941 Self::rmrk_property(License, &resource.license)?,942 Self::rmrk_property(Thumb, &resource.thumb)?,943 ]944 .into_iter(),945 )?;946947 Self::deposit_event(Event::ResourceAdded {948 nft_id,949 resource_id,950 });951 Ok(())952 }953954 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]955 #[transactional]956 pub fn remove_resource(957 origin: OriginFor<T>,958 rmrk_collection_id: RmrkCollectionId,959 nft_id: RmrkNftId,960 resource_id: RmrkResourceId,961 ) -> DispatchResult {962 let sender = ensure_signed(origin.clone())?;963964 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;965 let collection =966 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;967 collection.check_is_external()?;968969 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;970971 Self::deposit_event(Event::ResourceRemoval {972 nft_id,973 resource_id,974 });975 Ok(())976 }977 }978}979980impl<T: Config> Pallet<T> {981 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {982 let key = rmrk_key.to_key::<T>()?;983984 let scoped_key = PropertyScope::Rmrk985 .apply(key)986 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;987988 Ok(scoped_key)989 }990991 // todo think about renaming these992 pub fn rmrk_property<E: Encode>(993 rmrk_key: RmrkProperty,994 value: &E,995 ) -> Result<Property, DispatchError> {996 let key = rmrk_key.to_key::<T>()?;997998 let value = value999 .encode()1000 .try_into()1001 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;10021003 let property = Property { key, value };10041005 Ok(property)1006 }10071008 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1009 vec.decode()1010 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1011 }10121013 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1014 where1015 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1016 {1017 vec.rebind()1018 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1019 }10201021 fn init_collection(1022 sender: T::CrossAccountId,1023 data: CreateCollectionData<T::AccountId>,1024 properties: impl Iterator<Item = Property>,1025 ) -> Result<CollectionId, DispatchError> {1026 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10271028 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1029 return Err(<Error<T>>::NoAvailableCollectionId.into());1030 }10311032 <PalletCommon<T>>::set_scoped_collection_properties(1033 collection_id?,1034 PropertyScope::Rmrk,1035 properties,1036 )?;10371038 collection_id1039 }10401041 pub fn create_nft(1042 sender: &T::CrossAccountId,1043 owner: &T::CrossAccountId,1044 collection: &NonfungibleHandle<T>,1045 properties: impl Iterator<Item = Property>,1046 ) -> Result<TokenId, DispatchError> {1047 let data = CreateNftExData {1048 properties: BoundedVec::default(),1049 owner: owner.clone(),1050 };10511052 let budget = budget::Value::new(NESTING_BUDGET);10531054 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10551056 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10571058 <PalletNft<T>>::set_scoped_token_properties(1059 collection.id,1060 nft_id,1061 PropertyScope::Rmrk,1062 properties,1063 )?;10641065 Ok(nft_id)1066 }10671068 fn destroy_nft(1069 sender: T::CrossAccountId,1070 collection_id: CollectionId,1071 token_id: TokenId,1072 ) -> DispatchResult {1073 let collection =1074 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10751076 let token_data =1077 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10781079 let from = token_data.owner;10801081 let budget = budget::Value::new(NESTING_BUDGET);10821083 <PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1084 }10851086 fn resource_add(1087 sender: T::AccountId,1088 collection_id: CollectionId,1089 token_id: TokenId,1090 resource_properties: impl Iterator<Item = Property>,1091 ) -> Result<RmrkResourceId, DispatchError> {1092 let collection =1093 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1094 ensure!(collection.owner == sender, Error::<T>::NoPermission);10951096 let sender = T::CrossAccountId::from_sub(sender);1097 let budget = budget::Value::new(NESTING_BUDGET);10981099 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1100 .map_err(Self::map_unique_err_to_proxy)?;11011102 let pending = sender != nft_owner;11031104 let resource_collection_id: CollectionId =1105 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1106 let resource_collection =1107 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11081109 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11101111 let resource_id = Self::create_nft(1112 &sender,1113 &nft_owner,1114 &resource_collection,1115 resource_properties.chain(1116 [1117 Self::rmrk_property(PendingResourceAccept, &pending)?,1118 Self::rmrk_property(PendingResourceRemoval, &false)?,1119 ]1120 .into_iter(),1121 ),1122 )1123 .map_err(|err| match err {1124 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1125 err => Self::map_unique_err_to_proxy(err),1126 })?;11271128 Ok(resource_id.0)1129 }11301131 fn resource_remove(1132 sender: T::AccountId,1133 collection_id: CollectionId,1134 nft_id: TokenId,1135 resource_id: TokenId,1136 ) -> DispatchResult {1137 let collection =1138 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1139 ensure!(collection.owner == sender, Error::<T>::NoPermission);11401141 let resource_collection_id: CollectionId =1142 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1143 let resource_collection =1144 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1145 ensure!(1146 <PalletNft<T>>::token_exists(&resource_collection, resource_id),1147 Error::<T>::ResourceDoesntExist1148 );11491150 let budget = up_data_structs::budget::Value::new(10);1151 let topmost_owner =1152 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;11531154 let sender = T::CrossAccountId::from_sub(sender);1155 if topmost_owner == sender {1156 <PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1157 .map_err(Self::map_unique_err_to_proxy)?;1158 } else {1159 <PalletNft<T>>::set_scoped_token_property(1160 resource_collection_id,1161 resource_id,1162 PropertyScope::Rmrk,1163 Self::rmrk_property(PendingResourceRemoval, &true)?,1164 )?;1165 }11661167 Ok(())1168 }11691170 fn change_collection_owner(1171 collection_id: CollectionId,1172 collection_type: misc::CollectionType,1173 sender: T::AccountId,1174 new_owner: T::AccountId,1175 ) -> DispatchResult {1176 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1177 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11781179 let mut collection = collection.into_inner();11801181 collection.owner = new_owner;1182 collection.save()1183 }11841185 fn check_collection_owner(1186 collection: &NonfungibleHandle<T>,1187 account: &T::CrossAccountId,1188 ) -> DispatchResult {1189 collection1190 .check_is_owner(account)1191 .map_err(Self::map_unique_err_to_proxy)1192 }11931194 pub fn last_collection_idx() -> RmrkCollectionId {1195 <CollectionIndex<T>>::get()1196 }11971198 pub fn unique_collection_id(1199 rmrk_collection_id: RmrkCollectionId,1200 ) -> Result<CollectionId, DispatchError> {1201 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1202 .map_err(|_| <Error<T>>::CollectionUnknown.into())1203 }12041205 pub fn rmrk_collection_id(1206 unique_collection_id: CollectionId,1207 ) -> Result<RmrkCollectionId, DispatchError> {1208 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1209 .map_err(|_| <Error<T>>::CollectionUnknown.into())1210 }12111212 pub fn get_nft_collection(1213 collection_id: CollectionId,1214 ) -> Result<NonfungibleHandle<T>, DispatchError> {1215 let collection = <CollectionHandle<T>>::try_get(collection_id)1216 .map_err(|_| <Error<T>>::CollectionUnknown)?;12171218 match collection.mode {1219 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1220 _ => Err(<Error<T>>::CollectionUnknown.into()),1221 }1222 }12231224 pub fn collection_exists(collection_id: CollectionId) -> bool {1225 <CollectionHandle<T>>::try_get(collection_id).is_ok()1226 }12271228 pub fn get_collection_property(1229 collection_id: CollectionId,1230 key: RmrkProperty,1231 ) -> Result<PropertyValue, DispatchError> {1232 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1233 .get(&Self::rmrk_property_key(key)?)1234 .ok_or(<Error<T>>::CollectionUnknown)?1235 .clone();12361237 Ok(collection_property)1238 }12391240 pub fn get_collection_property_decoded<V: Decode>(1241 collection_id: CollectionId,1242 key: RmrkProperty,1243 ) -> Result<V, DispatchError> {1244 Self::decode_property(Self::get_collection_property(collection_id, key)?)1245 }12461247 pub fn get_collection_type(1248 collection_id: CollectionId,1249 ) -> Result<misc::CollectionType, DispatchError> {1250 Self::get_collection_property_decoded(collection_id, CollectionType)1251 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1252 }12531254 pub fn ensure_collection_type(1255 collection_id: CollectionId,1256 collection_type: misc::CollectionType,1257 ) -> DispatchResult {1258 let actual_type = Self::get_collection_type(collection_id)?;1259 ensure!(1260 actual_type == collection_type,1261 <CommonError<T>>::NoPermission1262 );12631264 Ok(())1265 }12661267 pub fn get_typed_nft_collection(1268 collection_id: CollectionId,1269 collection_type: misc::CollectionType,1270 ) -> Result<NonfungibleHandle<T>, DispatchError> {1271 Self::ensure_collection_type(collection_id, collection_type)?;12721273 Self::get_nft_collection(collection_id)1274 }12751276 pub fn get_typed_nft_collection_mapped(1277 rmrk_collection_id: RmrkCollectionId,1278 collection_type: misc::CollectionType,1279 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1280 let unique_collection_id = match collection_type {1281 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1282 _ => rmrk_collection_id.into(),1283 };12841285 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12861287 Ok((collection, unique_collection_id))1288 }12891290 pub fn get_nft_property(1291 collection_id: CollectionId,1292 nft_id: TokenId,1293 key: RmrkProperty,1294 ) -> Result<PropertyValue, DispatchError> {1295 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1296 .get(&Self::rmrk_property_key(key)?)1297 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1298 .clone();12991300 Ok(nft_property)1301 }13021303 pub fn get_nft_property_decoded<V: Decode>(1304 collection_id: CollectionId,1305 nft_id: TokenId,1306 key: RmrkProperty,1307 ) -> Result<V, DispatchError> {1308 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1309 }13101311 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1312 <TokenData<T>>::contains_key((collection_id, nft_id))1313 }13141315 pub fn get_nft_type(1316 collection_id: CollectionId,1317 token_id: TokenId,1318 ) -> Result<NftType, DispatchError> {1319 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1320 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1321 }13221323 pub fn ensure_nft_type(1324 collection_id: CollectionId,1325 token_id: TokenId,1326 nft_type: NftType,1327 ) -> DispatchResult {1328 let actual_type = Self::get_nft_type(collection_id, token_id)?;1329 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13301331 Ok(())1332 }13331334 pub fn ensure_nft_owner(1335 collection_id: CollectionId,1336 token_id: TokenId,1337 possible_owner: &T::CrossAccountId,1338 nesting_budget: &dyn budget::Budget,1339 ) -> DispatchResult {1340 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1341 possible_owner.clone(),1342 collection_id,1343 token_id,1344 None,1345 nesting_budget,1346 )1347 .map_err(Self::map_unique_err_to_proxy)?;13481349 ensure!(is_owned, <Error<T>>::NoPermission);13501351 Ok(())1352 }13531354 pub fn filter_user_properties<Key, Value, R, Mapper>(1355 collection_id: CollectionId,1356 token_id: Option<TokenId>,1357 filter_keys: Option<Vec<RmrkPropertyKey>>,1358 mapper: Mapper,1359 ) -> Result<Vec<R>, DispatchError>1360 where1361 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1362 Value: Decode + Default,1363 Mapper: Fn(Key, Value) -> R,1364 {1365 filter_keys1366 .map(|keys| {1367 let properties = keys1368 .into_iter()1369 .filter_map(|key| {1370 let key: Key = key.try_into().ok()?;13711372 let value = match token_id {1373 Some(token_id) => Self::get_nft_property_decoded(1374 collection_id,1375 token_id,1376 UserProperty(key.as_ref()),1377 ),1378 None => Self::get_collection_property_decoded(1379 collection_id,1380 UserProperty(key.as_ref()),1381 ),1382 }1383 .ok()?;13841385 Some(mapper(key, value))1386 })1387 .collect();13881389 Ok(properties)1390 })1391 .unwrap_or_else(|| {1392 let properties =1393 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13941395 Ok(properties)1396 })1397 }13981399 pub fn iterate_user_properties<Key, Value, R, Mapper>(1400 collection_id: CollectionId,1401 token_id: Option<TokenId>,1402 mapper: Mapper,1403 ) -> Result<impl Iterator<Item = R>, DispatchError>1404 where1405 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1406 Value: Decode + Default,1407 Mapper: Fn(Key, Value) -> R,1408 {1409 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14101411 let properties = match token_id {1412 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1413 None => <PalletCommon<T>>::collection_properties(collection_id),1414 };14151416 let properties = properties.into_iter().filter_map(move |(key, value)| {1417 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14181419 let key: Key = key.to_vec().try_into().ok()?;1420 let value: Value = value.decode().ok()?;14211422 Some(mapper(key, value))1423 });14241425 Ok(properties)1426 }14271428 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1429 map_unique_err_to_proxy! {1430 match err {1431 CommonError::NoPermission => NoPermission,1432 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1433 CommonError::PublicMintingNotAllowed => NoPermission,1434 CommonError::TokenNotFound => NoAvailableNftId,1435 CommonError::ApprovedValueTooLow => NoPermission,1436 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1437 StructureError::TokenNotFound => NoAvailableNftId,1438 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1439 }1440 }1441 }1442}pallets/proxy-rmrk-core/src/weights.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/weights.rs
@@ -0,0 +1,74 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_proxy_rmrk_core
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-06-09, STEPS: `50`, REPEAT: 200, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-proxy-rmrk-core
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=200
+// --heap-pages=4096
+// --output=./pallets/proxy-rmrk-core/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_proxy_rmrk_core.
+pub trait WeightInfo {
+ fn create_collection() -> Weight;
+}
+
+/// Weights for pallet_proxy_rmrk_core using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+ // Storage: Common CreatedCollectionCount (r:1 w:1)
+ // Storage: Common DestroyedCollectionCount (r:1 w:0)
+ // Storage: System Account (r:2 w:2)
+ // Storage: RmrkCore CollectionIndex (r:1 w:1)
+ // Storage: Common CollectionPropertyPermissions (r:0 w:1)
+ // Storage: Common CollectionProperties (r:0 w:1)
+ // Storage: Common CollectionById (r:0 w:1)
+ // Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+ // Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+ fn create_collection() -> Weight {
+ (76_647_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(5 as Weight))
+ .saturating_add(T::DbWeight::get().writes(9 as Weight))
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ // Storage: Common CreatedCollectionCount (r:1 w:1)
+ // Storage: Common DestroyedCollectionCount (r:1 w:0)
+ // Storage: System Account (r:2 w:2)
+ // Storage: RmrkCore CollectionIndex (r:1 w:1)
+ // Storage: Common CollectionPropertyPermissions (r:0 w:1)
+ // Storage: Common CollectionProperties (r:0 w:1)
+ // Storage: Common CollectionById (r:0 w:1)
+ // Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+ // Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+ fn create_collection() -> Weight {
+ (76_647_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(5 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(9 as Weight))
+ }
+}
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -827,6 +827,7 @@
list_benchmark!(list, extra, pallet_fungible, Fungible);
list_benchmark!(list, extra, pallet_refungible, Refungible);
list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);
+ list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);
// list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);
let storage_info = AllPalletsReversedWithSystemFirst::storage_info();
@@ -870,6 +871,7 @@
add_benchmark!(params, batches, pallet_fungible, Fungible);
add_benchmark!(params, batches, pallet_refungible, Refungible);
add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);
+ add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);
// add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);
if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -919,6 +919,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}
runtime/quartz/src/lib.rsdiffbeforeafterboth--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -918,6 +918,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}
runtime/unique/src/lib.rsdiffbeforeafterboth--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -917,6 +917,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}