difftreelog
feat initial rmrk send impl
in: master
10 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1298,6 +1298,7 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo;
fn transfer_from(
&self,
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -226,6 +226,7 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ _nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
ensure!(
token == TokenId::default(),
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -308,14 +308,15 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
with_weight(
if amount == 1 {
- <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))
+ <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender), nesting_budget)
} else {
- <Pallet<T>>::set_allowance(self, &sender, token, None)
+ <Pallet<T>>::set_allowance(self, &sender, token, None, nesting_budget)
},
<CommonWeights<T>>::approve(),
)
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -269,8 +269,11 @@
let caller = T::CrossAccountId::from_eth(caller);
let approved = T::CrossAccountId::from_eth(approved);
let token = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
- <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
+ <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved), &budget)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -878,6 +878,7 @@
sender: &T::CrossAccountId,
token: TokenId,
spender: Option<&T::CrossAccountId>,
+ nesting_budget: &dyn Budget,
) -> DispatchResult {
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
@@ -889,9 +890,16 @@
if let Some(spender) = spender {
<PalletCommon<T>>::ensure_correct_receiver(spender)?;
}
- let token_data =
- <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
- if &token_data.owner != sender {
+
+ let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+ sender.clone(),
+ collection.id,
+ token,
+ None,
+ nesting_budget
+ )?;
+
+ if !is_owned {
ensure!(
collection.ignores_owned_amount(sender),
<CommonError<T>>::CantApproveMoreThanOwned
@@ -918,6 +926,9 @@
// `from`, `to` checked in [`transfer`]
collection.check_allowlist(spender)?;
}
+ if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
+ return Ok(());
+ }
if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
// TODO: should collection owner be allowed to perform this transfer?
ensure!(
@@ -930,9 +941,6 @@
)?,
<CommonError<T>>::ApprovedValueTooLow,
);
- return Ok(());
- }
- if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
return Ok(());
}
ensure!(
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::*;24use pallet_common::{25 Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::Pallet as PalletStructure;29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142#[frame_support::pallet]43pub mod pallet {44 use super::*;45 use pallet_evm::account;4647 #[pallet::config]48 pub trait Config:49 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config50 {51 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;52 }5354 #[pallet::storage]55 #[pallet::getter(fn collection_index)]56 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5758 #[pallet::storage]59 #[pallet::getter(fn collection_index_map)]60 pub type CollectionIndexMap<T: Config> =61 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6263 #[pallet::pallet]64 #[pallet::generate_store(pub(super) trait Store)]65 pub struct Pallet<T>(_);6667 #[pallet::event]68 #[pallet::generate_deposit(pub(super) fn deposit_event)]69 pub enum Event<T: Config> {70 CollectionCreated {71 issuer: T::AccountId,72 collection_id: RmrkCollectionId,73 },74 CollectionDestroyed {75 issuer: T::AccountId,76 collection_id: RmrkCollectionId,77 },78 IssuerChanged {79 old_issuer: T::AccountId,80 new_issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 CollectionLocked {84 issuer: T::AccountId,85 collection_id: RmrkCollectionId,86 },87 NftMinted {88 owner: T::AccountId,89 collection_id: RmrkCollectionId,90 nft_id: RmrkNftId,91 },92 NFTBurned {93 owner: T::AccountId,94 nft_id: RmrkNftId,95 },96 PropertySet {97 collection_id: RmrkCollectionId,98 maybe_nft_id: Option<RmrkNftId>,99 key: RmrkKeyString,100 value: RmrkValueString,101 },102 ResourceAdded {103 nft_id: RmrkNftId,104 resource_id: RmrkResourceId,105 },106 ResourceRemoval {107 nft_id: RmrkNftId,108 resource_id: RmrkResourceId,109 },110 }111112 #[pallet::error]113 pub enum Error<T> {114 /* Unique-specific events */115 CorruptedCollectionType,116 NftTypeEncodeError,117 RmrkPropertyKeyIsTooLong,118 RmrkPropertyValueIsTooLong,119120 /* RMRK compatible events */121 CollectionNotEmpty,122 NoAvailableCollectionId,123 NoAvailableNftId,124 CollectionUnknown,125 NoPermission,126 CollectionFullOrLocked,127 ResourceDoesntExist,128 }129130 #[pallet::call]131 impl<T: Config> Pallet<T> {132 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]133 #[transactional]134 pub fn create_collection(135 origin: OriginFor<T>,136 metadata: RmrkString,137 max: Option<u32>,138 symbol: RmrkCollectionSymbol,139 ) -> DispatchResult {140 let sender = ensure_signed(origin)?;141142 let limits = CollectionLimits {143 owner_can_transfer: Some(false),144 token_limit: max,145 ..Default::default()146 };147148 let data = CreateCollectionData {149 limits: Some(limits),150 token_prefix: symbol151 .into_inner()152 .try_into()153 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,154 ..Default::default()155 };156157 <CollectionIndex<T>>::mutate(|n| *n += 1);158159 let unique_collection_id = Self::init_collection(160 T::CrossAccountId::from_sub(sender.clone()),161 data,162 [163 Self::rmrk_property(Metadata, &metadata)?,164 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,165 ]166 .into_iter(),167 )?;168 let rmrk_collection_id = <CollectionIndex<T>>::get();169170 <CollectionIndexMap<T>>::insert(rmrk_collection_id, unique_collection_id);171172 Self::deposit_event(Event::CollectionCreated {173 issuer: sender,174 collection_id: rmrk_collection_id,175 });176177 Ok(())178 }179180 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]181 #[transactional]182 pub fn destroy_collection(183 origin: OriginFor<T>,184 collection_id: RmrkCollectionId,185 ) -> DispatchResult {186 let sender = ensure_signed(origin)?;187 let cross_sender = T::CrossAccountId::from_sub(sender.clone());188189 let collection = Self::get_typed_nft_collection(190 Self::unique_collection_id(collection_id)?,191 misc::CollectionType::Regular,192 )?;193194 ensure!(195 collection.total_supply() == 0,196 <Error<T>>::CollectionNotEmpty197 );198199 <PalletNft<T>>::destroy_collection(collection, &cross_sender)200 .map_err(Self::map_common_err_to_proxy)?;201202 Self::deposit_event(Event::CollectionDestroyed {203 issuer: sender,204 collection_id,205 });206207 Ok(())208 }209210 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]211 #[transactional]212 pub fn change_collection_issuer(213 origin: OriginFor<T>,214 collection_id: RmrkCollectionId,215 new_issuer: <T::Lookup as StaticLookup>::Source,216 ) -> DispatchResult {217 let sender = ensure_signed(origin)?;218219 let new_issuer = T::Lookup::lookup(new_issuer)?;220221 Self::change_collection_owner(222 Self::unique_collection_id(collection_id)?,223 misc::CollectionType::Regular,224 sender.clone(),225 new_issuer.clone(),226 )?;227228 Self::deposit_event(Event::IssuerChanged {229 old_issuer: sender,230 new_issuer,231 collection_id,232 });233234 Ok(())235 }236237 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]238 #[transactional]239 pub fn lock_collection(240 origin: OriginFor<T>,241 collection_id: RmrkCollectionId,242 ) -> DispatchResult {243 let sender = ensure_signed(origin)?;244 let cross_sender = T::CrossAccountId::from_sub(sender.clone());245246 let collection = Self::get_typed_nft_collection(247 Self::unique_collection_id(collection_id)?,248 misc::CollectionType::Regular,249 )?;250251 Self::check_collection_owner(&collection, &cross_sender)?;252253 let token_count = collection.total_supply();254255 let mut collection = collection.into_inner();256 collection.limits.token_limit = Some(token_count);257 collection.save()?;258259 Self::deposit_event(Event::CollectionLocked {260 issuer: sender,261 collection_id,262 });263264 Ok(())265 }266267 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]268 #[transactional]269 pub fn mint_nft(270 origin: OriginFor<T>,271 owner: T::AccountId,272 collection_id: RmrkCollectionId,273 recipient: Option<T::AccountId>,274 royalty_amount: Option<Permill>,275 metadata: RmrkString,276 ) -> DispatchResult {277 let sender = ensure_signed(origin)?;278 let sender = T::CrossAccountId::from_sub(sender);279 let cross_owner = T::CrossAccountId::from_sub(owner.clone());280281 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {282 recipient: recipient.unwrap_or_else(|| owner.clone()),283 amount,284 });285286 let collection = Self::get_typed_nft_collection(287 Self::unique_collection_id(collection_id)?,288 misc::CollectionType::Regular,289 )?;290291 let nft_id = Self::create_nft(292 &sender,293 &cross_owner,294 &collection,295 [296 Self::rmrk_property(TokenType, &NftType::Regular)?,297 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,298 Self::rmrk_property(Metadata, &metadata)?,299 Self::rmrk_property(Equipped, &false)?,300 Self::rmrk_property(301 ResourceCollection,302 &Self::init_collection(303 sender.clone(),304 CreateCollectionData {305 ..Default::default()306 },307 [Self::rmrk_property(308 CollectionType,309 &misc::CollectionType::Resource,310 )?]311 .into_iter(),312 )?,313 )?, // todo possibly add limits to the collection if rmrk warrants them314 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,315 ]316 .into_iter(),317 )318 .map_err(|err| match err {319 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),320 err => Self::map_common_err_to_proxy(err),321 })?;322323 Self::deposit_event(Event::NftMinted {324 owner,325 collection_id,326 nft_id: nft_id.0,327 });328329 Ok(())330 }331332 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]333 #[transactional]334 pub fn burn_nft(335 origin: OriginFor<T>,336 collection_id: RmrkCollectionId,337 nft_id: RmrkNftId,338 ) -> DispatchResult {339 let sender = ensure_signed(origin)?;340 let cross_sender = T::CrossAccountId::from_sub(sender.clone());341342 Self::destroy_nft(343 cross_sender,344 Self::unique_collection_id(collection_id)?,345 misc::CollectionType::Regular,346 nft_id.into(),347 )?;348349 Self::deposit_event(Event::NFTBurned {350 owner: sender,351 nft_id,352 });353354 Ok(())355 }356357 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]358 #[transactional]359 pub fn set_property(360 origin: OriginFor<T>,361 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,362 maybe_nft_id: Option<RmrkNftId>,363 key: RmrkKeyString,364 value: RmrkValueString,365 ) -> DispatchResult {366 let sender = ensure_signed(origin)?;367 let sender = T::CrossAccountId::from_sub(sender);368369 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;370371 match maybe_nft_id {372 Some(nft_id) => {373 let token_id: TokenId = nft_id.into();374375 Self::ensure_nft_owner(collection_id, token_id, &sender)?;376 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;377378 <PalletNft<T>>::set_scoped_token_property(379 collection_id,380 token_id,381 PropertyScope::Rmrk,382 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,383 )?;384 }385 None => {386 let collection = Self::get_typed_nft_collection(387 collection_id,388 misc::CollectionType::Regular,389 )?;390391 Self::check_collection_owner(&collection, &sender)?;392393 <PalletCommon<T>>::set_scoped_collection_property(394 collection_id,395 PropertyScope::Rmrk,396 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,397 )?;398 }399 }400401 Self::deposit_event(Event::PropertySet {402 collection_id: rmrk_collection_id,403 maybe_nft_id,404 key,405 value,406 });407408 Ok(())409 }410411 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]412 #[transactional]413 pub fn add_basic_resource(414 origin: OriginFor<T>,415 collection_id: RmrkCollectionId,416 nft_id: RmrkNftId,417 resource: RmrkBasicResource,418 ) -> DispatchResult {419 let sender = ensure_signed(origin.clone())?;420421 let resource_id = Self::resource_add(422 sender,423 Self::unique_collection_id(collection_id)?,424 nft_id.into(),425 [426 Self::rmrk_property(TokenType, &NftType::Resource)?,427 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,428 Self::rmrk_property(Src, &resource.src)?,429 Self::rmrk_property(Metadata, &resource.metadata)?,430 Self::rmrk_property(License, &resource.license)?,431 Self::rmrk_property(Thumb, &resource.thumb)?,432 ]433 .into_iter(),434 )?;435436 Self::deposit_event(Event::ResourceAdded {437 nft_id,438 resource_id,439 });440 Ok(())441 }442443 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]444 #[transactional]445 pub fn add_composable_resource(446 origin: OriginFor<T>,447 collection_id: RmrkCollectionId,448 nft_id: RmrkNftId,449 _resource_id: RmrkBoundedResource,450 resource: RmrkComposableResource,451 ) -> DispatchResult {452 let sender = ensure_signed(origin.clone())?;453454 let resource_id = Self::resource_add(455 sender,456 Self::unique_collection_id(collection_id)?,457 nft_id.into(),458 [459 Self::rmrk_property(TokenType, &NftType::Resource)?,460 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,461 Self::rmrk_property(Parts, &resource.parts)?,462 Self::rmrk_property(Base, &resource.base)?,463 Self::rmrk_property(Src, &resource.src)?,464 Self::rmrk_property(Metadata, &resource.metadata)?,465 Self::rmrk_property(License, &resource.license)?,466 Self::rmrk_property(Thumb, &resource.thumb)?,467 ]468 .into_iter(),469 )?;470471 Self::deposit_event(Event::ResourceAdded {472 nft_id,473 resource_id,474 });475 Ok(())476 }477478 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]479 #[transactional]480 pub fn add_slot_resource(481 origin: OriginFor<T>,482 collection_id: RmrkCollectionId,483 nft_id: RmrkNftId,484 resource: RmrkSlotResource,485 ) -> DispatchResult {486 let sender = ensure_signed(origin.clone())?;487488 let resource_id = Self::resource_add(489 sender,490 Self::unique_collection_id(collection_id)?,491 nft_id.into(),492 [493 Self::rmrk_property(TokenType, &NftType::Resource)?,494 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,495 Self::rmrk_property(Base, &resource.base)?,496 Self::rmrk_property(Src, &resource.src)?,497 Self::rmrk_property(Metadata, &resource.metadata)?,498 Self::rmrk_property(Slot, &resource.slot)?,499 Self::rmrk_property(License, &resource.license)?,500 Self::rmrk_property(Thumb, &resource.thumb)?,501 ]502 .into_iter(),503 )?;504505 Self::deposit_event(Event::ResourceAdded {506 nft_id,507 resource_id,508 });509 Ok(())510 }511512 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]513 #[transactional]514 pub fn remove_resource(515 origin: OriginFor<T>,516 collection_id: RmrkCollectionId,517 nft_id: RmrkNftId,518 resource_id: RmrkResourceId,519 ) -> DispatchResult {520 let sender = ensure_signed(origin.clone())?;521522 Self::resource_remove(523 sender,524 Self::unique_collection_id(collection_id)?,525 nft_id.into(),526 resource_id.into(),527 )?;528529 Self::deposit_event(Event::ResourceRemoval {530 nft_id,531 resource_id,532 });533 Ok(())534 }535 }536}537538impl<T: Config> Pallet<T> {539 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {540 let key = rmrk_key.to_key::<T>()?;541542 let scoped_key = PropertyScope::Rmrk543 .apply(key)544 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;545546 Ok(scoped_key)547 }548549 // todo think about renaming these550 pub fn rmrk_property<E: Encode>(551 rmrk_key: RmrkProperty,552 value: &E,553 ) -> Result<Property, DispatchError> {554 let key = rmrk_key.to_key::<T>()?;555556 let value = value557 .encode()558 .try_into()559 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;560561 let property = Property { key, value };562563 Ok(property)564 }565566 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {567 vec.decode()568 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())569 }570571 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>572 where573 BoundedVec<u8, S>: TryFrom<Vec<u8>>,574 {575 vec.rebind()576 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())577 }578579 fn init_collection(580 sender: T::CrossAccountId,581 data: CreateCollectionData<T::AccountId>,582 properties: impl Iterator<Item = Property>,583 ) -> Result<CollectionId, DispatchError> {584 let collection_id = <PalletNft<T>>::init_collection(sender, data);585586 if let Err(DispatchError::Arithmetic(_)) = &collection_id {587 return Err(<Error<T>>::NoAvailableCollectionId.into());588 }589590 <PalletCommon<T>>::set_scoped_collection_properties(591 collection_id?,592 PropertyScope::Rmrk,593 properties,594 )?;595596 collection_id597 }598599 pub fn create_nft(600 sender: &T::CrossAccountId,601 owner: &T::CrossAccountId,602 collection: &NonfungibleHandle<T>,603 properties: impl Iterator<Item = Property>,604 ) -> Result<TokenId, DispatchError> {605 let data = CreateNftExData {606 properties: BoundedVec::default(),607 owner: owner.clone(),608 };609610 let budget = budget::Value::new(2);611612 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;613614 let nft_id = <PalletNft<T>>::current_token_id(collection.id);615616 <PalletNft<T>>::set_scoped_token_properties(617 collection.id,618 nft_id,619 PropertyScope::Rmrk,620 properties,621 )?;622623 Ok(nft_id)624 }625626 fn destroy_nft(627 sender: T::CrossAccountId,628 collection_id: CollectionId,629 collection_type: misc::CollectionType,630 token_id: TokenId,631 ) -> DispatchResult {632 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;633634 <PalletNft<T>>::burn(&collection, &sender, token_id)635 .map_err(Self::map_common_err_to_proxy)?;636637 Ok(())638 }639640 fn resource_add(641 sender: T::AccountId,642 collection_id: CollectionId,643 token_id: TokenId,644 resource_properties: impl Iterator<Item = Property>,645 ) -> Result<RmrkResourceId, DispatchError> {646 let collection =647 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;648 ensure!(collection.owner == sender, Error::<T>::NoPermission);649650 // Check NFT lock status // todo depends on market, maybe later651 //ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);652653 let sender = T::CrossAccountId::from_sub(sender);654 let budget = budget::Value::new(10);655 let pending = !<PalletStructure<T>>::check_indirectly_owned(656 sender.clone(),657 collection_id,658 token_id,659 None,660 &budget,661 )?;662663 let resource_collection_id: CollectionId =664 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;665 let resource_collection =666 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;667668 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them669670 let resource_id = Self::create_nft(671 &sender, // todo owner of the nft?672 &sender,673 &resource_collection,674 resource_properties.chain(675 [676 Self::rmrk_property(PendingResourceAccept, &pending)?,677 Self::rmrk_property(PendingResourceRemoval, &false)?,678 ]679 .into_iter(),680 ),681 )682 .map_err(|err| match err {683 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),684 err => Self::map_common_err_to_proxy(err),685 })?;686687 Ok(resource_id.0)688 }689690 fn resource_remove(691 sender: T::AccountId,692 collection_id: CollectionId,693 nft_id: TokenId,694 resource_id: TokenId,695 ) -> DispatchResult {696 let collection =697 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;698 ensure!(collection.owner == sender, Error::<T>::NoPermission);699700 let resource_collection_id: CollectionId =701 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;702 let resource_collection =703 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;704 ensure!(705 <PalletNft<T>>::token_exists(&resource_collection, resource_id),706 Error::<T>::ResourceDoesntExist707 );708709 let budget = up_data_structs::budget::Value::new(10);710 let topmost_owner =711 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;712713 let sender = T::CrossAccountId::from_sub(sender);714 if topmost_owner == sender {715 <PalletNft<T>>::burn(&collection, &sender, nft_id)716 .map_err(Self::map_common_err_to_proxy)?;717 } else {718 <PalletNft<T>>::set_scoped_token_property(719 collection_id,720 nft_id,721 PropertyScope::Rmrk,722 Self::rmrk_property(PendingResourceRemoval, &true)?,723 )?;724 }725726 Ok(())727 }728729 fn change_collection_owner(730 collection_id: CollectionId,731 collection_type: misc::CollectionType,732 sender: T::AccountId,733 new_owner: T::AccountId,734 ) -> DispatchResult {735 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;736 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;737738 let mut collection = collection.into_inner();739740 collection.owner = new_owner;741 collection.save()742 }743744 fn check_collection_owner(745 collection: &NonfungibleHandle<T>,746 account: &T::CrossAccountId,747 ) -> DispatchResult {748 collection749 .check_is_owner(account)750 .map_err(Self::map_common_err_to_proxy)751 }752753 pub fn last_collection_idx() -> RmrkCollectionId {754 <CollectionIndex<T>>::get()755 }756757 pub fn unique_collection_id(758 rmrk_collection_id: RmrkCollectionId,759 ) -> Result<CollectionId, DispatchError> {760 <CollectionIndexMap<T>>::try_get(rmrk_collection_id)761 .map_err(|_| <Error<T>>::CollectionUnknown.into())762 }763764 pub fn get_nft_collection(765 collection_id: CollectionId,766 ) -> Result<NonfungibleHandle<T>, DispatchError> {767 let collection = <CollectionHandle<T>>::try_get(collection_id)768 .map_err(|_| <Error<T>>::CollectionUnknown)?;769770 match collection.mode {771 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),772 _ => Err(<Error<T>>::CollectionUnknown.into()),773 }774 }775776 pub fn collection_exists(collection_id: CollectionId) -> bool {777 <CollectionHandle<T>>::try_get(collection_id).is_ok()778 }779780 pub fn get_collection_property(781 collection_id: CollectionId,782 key: RmrkProperty,783 ) -> Result<PropertyValue, DispatchError> {784 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)785 .get(&Self::rmrk_property_key(key)?)786 .ok_or(<Error<T>>::CollectionUnknown)?787 .clone();788789 Ok(collection_property)790 }791792 pub fn get_collection_property_decoded<V: Decode>(793 collection_id: CollectionId,794 key: RmrkProperty,795 ) -> Result<V, DispatchError> {796 Self::decode_property(Self::get_collection_property(collection_id, key)?)797 }798799 pub fn get_collection_type(800 collection_id: CollectionId,801 ) -> Result<misc::CollectionType, DispatchError> {802 Self::get_collection_property_decoded(collection_id, CollectionType)803 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())804 }805806 pub fn ensure_collection_type(807 collection_id: CollectionId,808 collection_type: misc::CollectionType,809 ) -> DispatchResult {810 let actual_type = Self::get_collection_type(collection_id)?;811 ensure!(812 actual_type == collection_type,813 <CommonError<T>>::NoPermission814 );815816 Ok(())817 }818819 pub fn get_typed_nft_collection(820 collection_id: CollectionId,821 collection_type: misc::CollectionType,822 ) -> Result<NonfungibleHandle<T>, DispatchError> {823 Self::ensure_collection_type(collection_id, collection_type)?;824825 Self::get_nft_collection(collection_id)826 }827828 pub fn get_nft_property(829 collection_id: CollectionId,830 nft_id: TokenId,831 key: RmrkProperty,832 ) -> Result<PropertyValue, DispatchError> {833 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))834 .get(&Self::rmrk_property_key(key)?)835 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?836 .clone();837838 Ok(nft_property)839 }840841 pub fn get_nft_property_decoded<V: Decode>(842 collection_id: CollectionId,843 nft_id: TokenId,844 key: RmrkProperty,845 ) -> Result<V, DispatchError> {846 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)847 }848849 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {850 <TokenData<T>>::contains_key((collection_id, nft_id))851 }852853 pub fn get_nft_type(854 collection_id: CollectionId,855 token_id: TokenId,856 ) -> Result<NftType, DispatchError> {857 Self::get_nft_property_decoded(collection_id, token_id, TokenType)858 .map_err(|_| <Error<T>>::NftTypeEncodeError.into())859 }860861 pub fn ensure_nft_type(862 collection_id: CollectionId,863 token_id: TokenId,864 nft_type: NftType,865 ) -> DispatchResult {866 let actual_type = Self::get_nft_type(collection_id, token_id)?;867 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);868869 Ok(())870 }871872 pub fn ensure_nft_owner(873 collection_id: CollectionId,874 token_id: TokenId,875 possible_owner: &T::CrossAccountId,876 ) -> DispatchResult {877 let token_data =878 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;879880 ensure!(881 token_data.owner == *possible_owner,882 <Error<T>>::NoPermission883 );884885 Ok(())886 }887888 pub fn filter_user_properties<Key, Value, R, Mapper>(889 collection_id: CollectionId,890 token_id: Option<TokenId>,891 filter_keys: Option<Vec<RmrkPropertyKey>>,892 mapper: Mapper,893 ) -> Result<Vec<R>, DispatchError>894 where895 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,896 Value: Decode + Default,897 Mapper: Fn(Key, Value) -> R,898 {899 filter_keys900 .map(|keys| {901 let properties = keys902 .into_iter()903 .filter_map(|key| {904 let key: Key = key.try_into().ok()?;905906 let value = match token_id {907 Some(token_id) => Self::get_nft_property_decoded(908 collection_id,909 token_id,910 UserProperty(key.as_ref()),911 ),912 None => Self::get_collection_property_decoded(913 collection_id,914 UserProperty(key.as_ref()),915 ),916 }917 .ok()?;918919 Some(mapper(key, value))920 })921 .collect();922923 Ok(properties)924 })925 .unwrap_or_else(|| {926 let properties =927 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();928929 Ok(properties)930 })931 }932933 pub fn iterate_user_properties<Key, Value, R, Mapper>(934 collection_id: CollectionId,935 token_id: Option<TokenId>,936 mapper: Mapper,937 ) -> Result<impl Iterator<Item = R>, DispatchError>938 where939 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,940 Value: Decode + Default,941 Mapper: Fn(Key, Value) -> R,942 {943 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;944945 let properties = match token_id {946 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),947 None => <PalletCommon<T>>::collection_properties(collection_id),948 };949950 let properties = properties.into_iter().filter_map(move |(key, value)| {951 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;952953 let key: Key = key.to_vec().try_into().ok()?;954 let value: Value = value.decode().ok()?;955956 Some(mapper(key, value))957 });958959 Ok(properties)960 }961962 fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {963 map_common_err_to_proxy! {964 match err {965 NoPermission => NoPermission,966 CollectionTokenLimitExceeded => CollectionFullOrLocked,967 PublicMintingNotAllowed => NoPermission,968 TokenNotFound => NoAvailableNftId969 }970 }971 }972}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;29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46 use super::*;47 use pallet_evm::account;4849 #[pallet::config]50 pub trait Config:51 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52 {53 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54 }5556 #[pallet::storage]57 #[pallet::getter(fn collection_index)]58 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960 #[pallet::storage]61 pub type UniqueCollectionId<T: Config> =62 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364 #[pallet::storage]65 pub type RmrkInernalCollectionId<T: Config> =66 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768 #[pallet::pallet]69 #[pallet::generate_store(pub(super) trait Store)]70 pub struct Pallet<T>(_);7172 #[pallet::event]73 #[pallet::generate_deposit(pub(super) fn deposit_event)]74 pub enum Event<T: Config> {75 CollectionCreated {76 issuer: T::AccountId,77 collection_id: RmrkCollectionId,78 },79 CollectionDestroyed {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 IssuerChanged {84 old_issuer: T::AccountId,85 new_issuer: T::AccountId,86 collection_id: RmrkCollectionId,87 },88 CollectionLocked {89 issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 NftMinted {93 owner: T::AccountId,94 collection_id: RmrkCollectionId,95 nft_id: RmrkNftId,96 },97 NFTBurned {98 owner: T::AccountId,99 nft_id: RmrkNftId,100 },101 PropertySet {102 collection_id: RmrkCollectionId,103 maybe_nft_id: Option<RmrkNftId>,104 key: RmrkKeyString,105 value: RmrkValueString,106 },107 ResourceAdded {108 nft_id: RmrkNftId,109 resource_id: RmrkResourceId,110 },111 ResourceRemoval {112 nft_id: RmrkNftId,113 resource_id: RmrkResourceId,114 },115 }116117 #[pallet::error]118 pub enum Error<T> {119 /* Unique-specific events */120 CorruptedCollectionType,121 NftTypeEncodeError,122 RmrkPropertyKeyIsTooLong,123 RmrkPropertyValueIsTooLong,124125 /* RMRK compatible events */126 CollectionNotEmpty,127 NoAvailableCollectionId,128 NoAvailableNftId,129 CollectionUnknown,130 NoPermission,131 CollectionFullOrLocked,132 ResourceDoesntExist,133 }134135 #[pallet::call]136 impl<T: Config> Pallet<T> {137 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]138 #[transactional]139 pub fn create_collection(140 origin: OriginFor<T>,141 metadata: RmrkString,142 max: Option<u32>,143 symbol: RmrkCollectionSymbol,144 ) -> DispatchResult {145 let sender = ensure_signed(origin)?;146147 let limits = CollectionLimits {148 owner_can_transfer: Some(false),149 token_limit: max,150 ..Default::default()151 };152153 let data = CreateCollectionData {154 limits: Some(limits),155 token_prefix: symbol156 .into_inner()157 .try_into()158 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,159 permissions: Some(CollectionPermissions {160 nesting: Some(NestingRule::Owner),161 ..Default::default()162 }),163 ..Default::default()164 };165166 let unique_collection_id = Self::init_collection(167 T::CrossAccountId::from_sub(sender.clone()),168 data,169 [170 Self::rmrk_property(Metadata, &metadata)?,171 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,172 ]173 .into_iter(),174 )?;175 let rmrk_collection_id = <CollectionIndex<T>>::get();176177 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);178 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);179180 <CollectionIndex<T>>::mutate(|n| *n += 1);181182 Self::deposit_event(Event::CollectionCreated {183 issuer: sender,184 collection_id: rmrk_collection_id,185 });186187 Ok(())188 }189190 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]191 #[transactional]192 pub fn destroy_collection(193 origin: OriginFor<T>,194 collection_id: RmrkCollectionId,195 ) -> DispatchResult {196 let sender = ensure_signed(origin)?;197 let cross_sender = T::CrossAccountId::from_sub(sender.clone());198199 let collection = Self::get_typed_nft_collection(200 Self::unique_collection_id(collection_id)?,201 misc::CollectionType::Regular,202 )?;203204 ensure!(205 collection.total_supply() == 0,206 <Error<T>>::CollectionNotEmpty207 );208209 <PalletNft<T>>::destroy_collection(collection, &cross_sender)210 .map_err(Self::map_common_err_to_proxy)?;211212 Self::deposit_event(Event::CollectionDestroyed {213 issuer: sender,214 collection_id,215 });216217 Ok(())218 }219220 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]221 #[transactional]222 pub fn change_collection_issuer(223 origin: OriginFor<T>,224 collection_id: RmrkCollectionId,225 new_issuer: <T::Lookup as StaticLookup>::Source,226 ) -> DispatchResult {227 let sender = ensure_signed(origin)?;228229 let new_issuer = T::Lookup::lookup(new_issuer)?;230231 Self::change_collection_owner(232 Self::unique_collection_id(collection_id)?,233 misc::CollectionType::Regular,234 sender.clone(),235 new_issuer.clone(),236 )?;237238 Self::deposit_event(Event::IssuerChanged {239 old_issuer: sender,240 new_issuer,241 collection_id,242 });243244 Ok(())245 }246247 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]248 #[transactional]249 pub fn lock_collection(250 origin: OriginFor<T>,251 collection_id: RmrkCollectionId,252 ) -> DispatchResult {253 let sender = ensure_signed(origin)?;254 let cross_sender = T::CrossAccountId::from_sub(sender.clone());255256 let collection = Self::get_typed_nft_collection(257 Self::unique_collection_id(collection_id)?,258 misc::CollectionType::Regular,259 )?;260261 Self::check_collection_owner(&collection, &cross_sender)?;262263 let token_count = collection.total_supply();264265 let mut collection = collection.into_inner();266 collection.limits.token_limit = Some(token_count);267 collection.save()?;268269 Self::deposit_event(Event::CollectionLocked {270 issuer: sender,271 collection_id,272 });273274 Ok(())275 }276277 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]278 #[transactional]279 pub fn mint_nft(280 origin: OriginFor<T>,281 owner: T::AccountId,282 collection_id: RmrkCollectionId,283 recipient: Option<T::AccountId>,284 royalty_amount: Option<Permill>,285 metadata: RmrkString,286 ) -> DispatchResult {287 let sender = ensure_signed(origin)?;288 let sender = T::CrossAccountId::from_sub(sender);289 let cross_owner = T::CrossAccountId::from_sub(owner.clone());290291 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {292 recipient: recipient.unwrap_or_else(|| owner.clone()),293 amount,294 });295296 let collection = Self::get_typed_nft_collection(297 Self::unique_collection_id(collection_id)?,298 misc::CollectionType::Regular,299 )?;300301 let nft_id = Self::create_nft(302 &sender,303 &cross_owner,304 &collection,305 [306 Self::rmrk_property(TokenType, &NftType::Regular)?,307 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,308 Self::rmrk_property(Metadata, &metadata)?,309 Self::rmrk_property(Equipped, &false)?,310 Self::rmrk_property(311 ResourceCollection,312 &Self::init_collection(313 sender.clone(),314 CreateCollectionData {315 ..Default::default()316 },317 [Self::rmrk_property(318 CollectionType,319 &misc::CollectionType::Resource,320 )?]321 .into_iter(),322 )?,323 )?, // todo possibly add limits to the collection if rmrk warrants them324 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,325 ]326 .into_iter(),327 )328 .map_err(|err| match err {329 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),330 err => Self::map_common_err_to_proxy(err),331 })?;332333 Self::deposit_event(Event::NftMinted {334 owner,335 collection_id,336 nft_id: nft_id.0,337 });338339 Ok(())340 }341342 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]343 #[transactional]344 pub fn burn_nft(345 origin: OriginFor<T>,346 collection_id: RmrkCollectionId,347 nft_id: RmrkNftId,348 ) -> DispatchResult {349 let sender = ensure_signed(origin)?;350 let cross_sender = T::CrossAccountId::from_sub(sender.clone());351352 Self::destroy_nft(353 cross_sender,354 Self::unique_collection_id(collection_id)?,355 misc::CollectionType::Regular,356 nft_id.into(),357 )?;358359 Self::deposit_event(Event::NFTBurned {360 owner: sender,361 nft_id,362 });363364 Ok(())365 }366367 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]368 #[transactional]369 pub fn send(370 origin: OriginFor<T>,371 rmrk_collection_id: RmrkCollectionId,372 rmrk_nft_id: RmrkNftId,373 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,374 ) -> DispatchResult {375 let sender = ensure_signed(origin.clone())?;376 let cross_sender = T::CrossAccountId::from_sub(sender.clone());377378 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;379 let nft_id = rmrk_nft_id.into();380381 let token_data = <TokenData<T>>::get((collection_id, nft_id))382 .ok_or(<Error<T>>::NoAvailableNftId)?;383384 let from = token_data.owner;385386 let collection = Self::get_typed_nft_collection(387 collection_id,388 misc::CollectionType::Regular,389 )?;390391 let budget = budget::Value::new(NESTING_BUDGET);392393 let target_owner;394395 match new_owner {396 RmrkAccountIdOrCollectionNftTuple::AccountId(account_id) => {397 target_owner = T::CrossAccountId::from_sub(account_id);398 },399 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(target_collection_id, target_nft_id) => {400 let target_collection_id = Self::unique_collection_id(target_collection_id)?;401402 target_owner = T::CrossTokenAddressMapping::token_to_address(403 target_collection_id,404 target_nft_id.into(),405 );406407 let spender = <PalletStructure<T>>::get_indirect_owner(408 target_collection_id,409 target_nft_id.into(),410 Some((collection_id, nft_id)),411 &budget,412 )?;413414 let is_approval_required = cross_sender != spender;415416 if is_approval_required {417 <PalletNft<T>>::set_allowance(418 &collection,419 &cross_sender,420 nft_id,421 Some(&spender),422 &budget423 ).map_err(Self::map_common_err_to_proxy)?;424425 return Ok(());426 }427 }428 }429430 <PalletNft<T>>::transfer_from(431 &collection,432 &cross_sender,433 &from,434 &target_owner,435 nft_id,436 &budget437 ).map_err(Self::map_common_err_to_proxy)?;438439 Ok(())440 }441442 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]443 #[transactional]444 pub fn set_property(445 origin: OriginFor<T>,446 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,447 maybe_nft_id: Option<RmrkNftId>,448 key: RmrkKeyString,449 value: RmrkValueString,450 ) -> DispatchResult {451 let sender = ensure_signed(origin)?;452 let sender = T::CrossAccountId::from_sub(sender);453454 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;455 let budget = budget::Value::new(NESTING_BUDGET);456457 match maybe_nft_id {458 Some(nft_id) => {459 let token_id: TokenId = nft_id.into();460461 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;462 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;463464 <PalletNft<T>>::set_scoped_token_property(465 collection_id,466 token_id,467 PropertyScope::Rmrk,468 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,469 )?;470 }471 None => {472 let collection = Self::get_typed_nft_collection(473 collection_id,474 misc::CollectionType::Regular,475 )?;476477 Self::check_collection_owner(&collection, &sender)?;478479 <PalletCommon<T>>::set_scoped_collection_property(480 collection_id,481 PropertyScope::Rmrk,482 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,483 )?;484 }485 }486487 Self::deposit_event(Event::PropertySet {488 collection_id: rmrk_collection_id,489 maybe_nft_id,490 key,491 value,492 });493494 Ok(())495 }496497 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]498 #[transactional]499 pub fn add_basic_resource(500 origin: OriginFor<T>,501 collection_id: RmrkCollectionId,502 nft_id: RmrkNftId,503 resource: RmrkBasicResource,504 ) -> DispatchResult {505 let sender = ensure_signed(origin.clone())?;506507 let resource_id = Self::resource_add(508 sender,509 Self::unique_collection_id(collection_id)?,510 nft_id.into(),511 [512 Self::rmrk_property(TokenType, &NftType::Resource)?,513 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,514 Self::rmrk_property(Src, &resource.src)?,515 Self::rmrk_property(Metadata, &resource.metadata)?,516 Self::rmrk_property(License, &resource.license)?,517 Self::rmrk_property(Thumb, &resource.thumb)?,518 ]519 .into_iter(),520 )?;521522 Self::deposit_event(Event::ResourceAdded {523 nft_id,524 resource_id,525 });526 Ok(())527 }528529 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]530 #[transactional]531 pub fn add_composable_resource(532 origin: OriginFor<T>,533 collection_id: RmrkCollectionId,534 nft_id: RmrkNftId,535 _resource_id: RmrkBoundedResource,536 resource: RmrkComposableResource,537 ) -> DispatchResult {538 let sender = ensure_signed(origin.clone())?;539540 let resource_id = Self::resource_add(541 sender,542 Self::unique_collection_id(collection_id)?,543 nft_id.into(),544 [545 Self::rmrk_property(TokenType, &NftType::Resource)?,546 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,547 Self::rmrk_property(Parts, &resource.parts)?,548 Self::rmrk_property(Base, &resource.base)?,549 Self::rmrk_property(Src, &resource.src)?,550 Self::rmrk_property(Metadata, &resource.metadata)?,551 Self::rmrk_property(License, &resource.license)?,552 Self::rmrk_property(Thumb, &resource.thumb)?,553 ]554 .into_iter(),555 )?;556557 Self::deposit_event(Event::ResourceAdded {558 nft_id,559 resource_id,560 });561 Ok(())562 }563564 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]565 #[transactional]566 pub fn add_slot_resource(567 origin: OriginFor<T>,568 collection_id: RmrkCollectionId,569 nft_id: RmrkNftId,570 resource: RmrkSlotResource,571 ) -> DispatchResult {572 let sender = ensure_signed(origin.clone())?;573574 let resource_id = Self::resource_add(575 sender,576 Self::unique_collection_id(collection_id)?,577 nft_id.into(),578 [579 Self::rmrk_property(TokenType, &NftType::Resource)?,580 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,581 Self::rmrk_property(Base, &resource.base)?,582 Self::rmrk_property(Src, &resource.src)?,583 Self::rmrk_property(Metadata, &resource.metadata)?,584 Self::rmrk_property(Slot, &resource.slot)?,585 Self::rmrk_property(License, &resource.license)?,586 Self::rmrk_property(Thumb, &resource.thumb)?,587 ]588 .into_iter(),589 )?;590591 Self::deposit_event(Event::ResourceAdded {592 nft_id,593 resource_id,594 });595 Ok(())596 }597598 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]599 #[transactional]600 pub fn remove_resource(601 origin: OriginFor<T>,602 collection_id: RmrkCollectionId,603 nft_id: RmrkNftId,604 resource_id: RmrkResourceId,605 ) -> DispatchResult {606 let sender = ensure_signed(origin.clone())?;607608 Self::resource_remove(609 sender,610 Self::unique_collection_id(collection_id)?,611 nft_id.into(),612 resource_id.into(),613 )?;614615 Self::deposit_event(Event::ResourceRemoval {616 nft_id,617 resource_id,618 });619 Ok(())620 }621 }622}623624impl<T: Config> Pallet<T> {625 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {626 let key = rmrk_key.to_key::<T>()?;627628 let scoped_key = PropertyScope::Rmrk629 .apply(key)630 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;631632 Ok(scoped_key)633 }634635 // todo think about renaming these636 pub fn rmrk_property<E: Encode>(637 rmrk_key: RmrkProperty,638 value: &E,639 ) -> Result<Property, DispatchError> {640 let key = rmrk_key.to_key::<T>()?;641642 let value = value643 .encode()644 .try_into()645 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;646647 let property = Property { key, value };648649 Ok(property)650 }651652 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {653 vec.decode()654 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())655 }656657 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>658 where659 BoundedVec<u8, S>: TryFrom<Vec<u8>>,660 {661 vec.rebind()662 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())663 }664665 fn init_collection(666 sender: T::CrossAccountId,667 data: CreateCollectionData<T::AccountId>,668 properties: impl Iterator<Item = Property>,669 ) -> Result<CollectionId, DispatchError> {670 let collection_id = <PalletNft<T>>::init_collection(sender, data);671672 if let Err(DispatchError::Arithmetic(_)) = &collection_id {673 return Err(<Error<T>>::NoAvailableCollectionId.into());674 }675676 <PalletCommon<T>>::set_scoped_collection_properties(677 collection_id?,678 PropertyScope::Rmrk,679 properties,680 )?;681682 collection_id683 }684685 pub fn create_nft(686 sender: &T::CrossAccountId,687 owner: &T::CrossAccountId,688 collection: &NonfungibleHandle<T>,689 properties: impl Iterator<Item = Property>,690 ) -> Result<TokenId, DispatchError> {691 let data = CreateNftExData {692 properties: BoundedVec::default(),693 owner: owner.clone(),694 };695696 let budget = budget::Value::new(NESTING_BUDGET);697698 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;699700 let nft_id = <PalletNft<T>>::current_token_id(collection.id);701702 <PalletNft<T>>::set_scoped_token_properties(703 collection.id,704 nft_id,705 PropertyScope::Rmrk,706 properties,707 )?;708709 Ok(nft_id)710 }711712 fn destroy_nft(713 sender: T::CrossAccountId,714 collection_id: CollectionId,715 collection_type: misc::CollectionType,716 token_id: TokenId,717 ) -> DispatchResult {718 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;719720 <PalletNft<T>>::burn(&collection, &sender, token_id)721 .map_err(Self::map_common_err_to_proxy)?;722723 Ok(())724 }725726 fn resource_add(727 sender: T::AccountId,728 collection_id: CollectionId,729 token_id: TokenId,730 resource_properties: impl Iterator<Item = Property>,731 ) -> Result<RmrkResourceId, DispatchError> {732 let collection =733 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;734 ensure!(collection.owner == sender, Error::<T>::NoPermission);735736 // Check NFT lock status // todo depends on market, maybe later737 //ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);738739 let sender = T::CrossAccountId::from_sub(sender);740 let budget = budget::Value::new(NESTING_BUDGET);741 let pending = Self::ensure_nft_owner(collection_id, token_id, &sender, &budget).is_err();742743 let resource_collection_id: CollectionId =744 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;745 let resource_collection =746 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;747748 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them749750 let resource_id = Self::create_nft(751 &sender, // todo owner of the nft?752 &sender,753 &resource_collection,754 resource_properties.chain(755 [756 Self::rmrk_property(PendingResourceAccept, &pending)?,757 Self::rmrk_property(PendingResourceRemoval, &false)?,758 ]759 .into_iter(),760 ),761 )762 .map_err(|err| match err {763 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),764 err => Self::map_common_err_to_proxy(err),765 })?;766767 Ok(resource_id.0)768 }769770 fn resource_remove(771 sender: T::AccountId,772 collection_id: CollectionId,773 nft_id: TokenId,774 resource_id: TokenId,775 ) -> DispatchResult {776 let collection =777 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;778 ensure!(collection.owner == sender, Error::<T>::NoPermission);779780 let resource_collection_id: CollectionId =781 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;782 let resource_collection =783 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;784 ensure!(785 <PalletNft<T>>::token_exists(&resource_collection, resource_id),786 Error::<T>::ResourceDoesntExist787 );788789 let budget = up_data_structs::budget::Value::new(10);790 let topmost_owner =791 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;792793 let sender = T::CrossAccountId::from_sub(sender);794 if topmost_owner == sender {795 <PalletNft<T>>::burn(&collection, &sender, nft_id)796 .map_err(Self::map_common_err_to_proxy)?;797 } else {798 <PalletNft<T>>::set_scoped_token_property(799 collection_id,800 nft_id,801 PropertyScope::Rmrk,802 Self::rmrk_property(PendingResourceRemoval, &true)?,803 )?;804 }805806 Ok(())807 }808809 fn change_collection_owner(810 collection_id: CollectionId,811 collection_type: misc::CollectionType,812 sender: T::AccountId,813 new_owner: T::AccountId,814 ) -> DispatchResult {815 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;816 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;817818 let mut collection = collection.into_inner();819820 collection.owner = new_owner;821 collection.save()822 }823824 fn check_collection_owner(825 collection: &NonfungibleHandle<T>,826 account: &T::CrossAccountId,827 ) -> DispatchResult {828 collection829 .check_is_owner(account)830 .map_err(Self::map_common_err_to_proxy)831 }832833 pub fn last_collection_idx() -> RmrkCollectionId {834 <CollectionIndex<T>>::get()835 }836837 pub fn unique_collection_id(838 rmrk_collection_id: RmrkCollectionId,839 ) -> Result<CollectionId, DispatchError> {840 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)841 .map_err(|_| <Error<T>>::CollectionUnknown.into())842 }843844 pub fn rmrk_collection_id(845 unique_collection_id: CollectionId846 ) -> Result<RmrkCollectionId, DispatchError> {847 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)848 .map_err(|_| <Error<T>>::CollectionUnknown.into())849 }850851 pub fn get_nft_collection(852 collection_id: CollectionId,853 ) -> Result<NonfungibleHandle<T>, DispatchError> {854 let collection = <CollectionHandle<T>>::try_get(collection_id)855 .map_err(|_| <Error<T>>::CollectionUnknown)?;856857 match collection.mode {858 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),859 _ => Err(<Error<T>>::CollectionUnknown.into()),860 }861 }862863 pub fn collection_exists(collection_id: CollectionId) -> bool {864 <CollectionHandle<T>>::try_get(collection_id).is_ok()865 }866867 pub fn get_collection_property(868 collection_id: CollectionId,869 key: RmrkProperty,870 ) -> Result<PropertyValue, DispatchError> {871 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)872 .get(&Self::rmrk_property_key(key)?)873 .ok_or(<Error<T>>::CollectionUnknown)?874 .clone();875876 Ok(collection_property)877 }878879 pub fn get_collection_property_decoded<V: Decode>(880 collection_id: CollectionId,881 key: RmrkProperty,882 ) -> Result<V, DispatchError> {883 Self::decode_property(Self::get_collection_property(collection_id, key)?)884 }885886 pub fn get_collection_type(887 collection_id: CollectionId,888 ) -> Result<misc::CollectionType, DispatchError> {889 Self::get_collection_property_decoded(collection_id, CollectionType)890 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())891 }892893 pub fn ensure_collection_type(894 collection_id: CollectionId,895 collection_type: misc::CollectionType,896 ) -> DispatchResult {897 let actual_type = Self::get_collection_type(collection_id)?;898 ensure!(899 actual_type == collection_type,900 <CommonError<T>>::NoPermission901 );902903 Ok(())904 }905906 pub fn get_typed_nft_collection(907 collection_id: CollectionId,908 collection_type: misc::CollectionType,909 ) -> Result<NonfungibleHandle<T>, DispatchError> {910 Self::ensure_collection_type(collection_id, collection_type)?;911912 Self::get_nft_collection(collection_id)913 }914915 pub fn get_nft_property(916 collection_id: CollectionId,917 nft_id: TokenId,918 key: RmrkProperty,919 ) -> Result<PropertyValue, DispatchError> {920 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))921 .get(&Self::rmrk_property_key(key)?)922 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?923 .clone();924925 Ok(nft_property)926 }927928 pub fn get_nft_property_decoded<V: Decode>(929 collection_id: CollectionId,930 nft_id: TokenId,931 key: RmrkProperty,932 ) -> Result<V, DispatchError> {933 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)934 }935936 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {937 <TokenData<T>>::contains_key((collection_id, nft_id))938 }939940 pub fn get_nft_type(941 collection_id: CollectionId,942 token_id: TokenId,943 ) -> Result<NftType, DispatchError> {944 Self::get_nft_property_decoded(collection_id, token_id, TokenType)945 .map_err(|_| <Error<T>>::NftTypeEncodeError.into())946 }947948 pub fn ensure_nft_type(949 collection_id: CollectionId,950 token_id: TokenId,951 nft_type: NftType,952 ) -> DispatchResult {953 let actual_type = Self::get_nft_type(collection_id, token_id)?;954 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);955956 Ok(())957 }958959 pub fn ensure_nft_owner(960 collection_id: CollectionId,961 token_id: TokenId,962 possible_owner: &T::CrossAccountId,963 nesting_budget: &dyn budget::Budget964 ) -> DispatchResult {965 let is_owned = <PalletStructure<T>>::check_indirectly_owned(966 possible_owner.clone(),967 collection_id,968 token_id,969 None,970 nesting_budget,971 )?;972973 ensure!(974 is_owned,975 <Error<T>>::NoPermission976 );977978 Ok(())979 }980981 pub fn filter_user_properties<Key, Value, R, Mapper>(982 collection_id: CollectionId,983 token_id: Option<TokenId>,984 filter_keys: Option<Vec<RmrkPropertyKey>>,985 mapper: Mapper,986 ) -> Result<Vec<R>, DispatchError>987 where988 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,989 Value: Decode + Default,990 Mapper: Fn(Key, Value) -> R,991 {992 filter_keys993 .map(|keys| {994 let properties = keys995 .into_iter()996 .filter_map(|key| {997 let key: Key = key.try_into().ok()?;998999 let value = match token_id {1000 Some(token_id) => Self::get_nft_property_decoded(1001 collection_id,1002 token_id,1003 UserProperty(key.as_ref()),1004 ),1005 None => Self::get_collection_property_decoded(1006 collection_id,1007 UserProperty(key.as_ref()),1008 ),1009 }1010 .ok()?;10111012 Some(mapper(key, value))1013 })1014 .collect();10151016 Ok(properties)1017 })1018 .unwrap_or_else(|| {1019 let properties =1020 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();10211022 Ok(properties)1023 })1024 }10251026 pub fn iterate_user_properties<Key, Value, R, Mapper>(1027 collection_id: CollectionId,1028 token_id: Option<TokenId>,1029 mapper: Mapper,1030 ) -> Result<impl Iterator<Item = R>, DispatchError>1031 where1032 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1033 Value: Decode + Default,1034 Mapper: Fn(Key, Value) -> R,1035 {1036 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;10371038 let properties = match token_id {1039 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1040 None => <PalletCommon<T>>::collection_properties(collection_id),1041 };10421043 let properties = properties.into_iter().filter_map(move |(key, value)| {1044 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;10451046 let key: Key = key.to_vec().try_into().ok()?;1047 let value: Value = value.decode().ok()?;10481049 Some(mapper(key, value))1050 });10511052 Ok(properties)1053 }10541055 fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {1056 map_common_err_to_proxy! {1057 match err {1058 NoPermission => NoPermission,1059 CollectionTokenLimitExceeded => CollectionFullOrLocked,1060 PublicMintingNotAllowed => NoPermission,1061 TokenNotFound => NoAvailableNftId,1062 ApprovedValueTooLow => NoPermission1063 }1064 }1065 }1066}pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -254,6 +254,7 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ _nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
with_weight(
<Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -149,19 +149,12 @@
})
}
- /// Check if token indirectly owned by specified user
- pub fn check_indirectly_owned(
- user: T::CrossAccountId,
+ pub fn get_indirect_owner(
collection: CollectionId,
token: TokenId,
for_nest: Option<(CollectionId, TokenId)>,
budget: &dyn Budget,
- ) -> Result<bool, DispatchError> {
- let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
- Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
- None => user,
- };
-
+ ) -> Result<T::CrossAccountId, DispatchError> {
// Tried to nest token in itself
if Some((collection, token)) == for_nest {
return Err(<Error<T>>::OuroborosDetected.into());
@@ -173,10 +166,8 @@
Parent::Token(collection, token) if Some((collection, token)) == for_nest => {
return Err(<Error<T>>::OuroborosDetected.into())
}
- // Found needed parent, token is indirecty owned
- Parent::User(user) if user == target_parent => return Ok(true),
// Token is owned by other user
- Parent::User(_) => return Ok(false),
+ Parent::User(user) => return Ok(user),
Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
// Continue parent chain
Parent::Token(_, _) => {}
@@ -199,6 +190,27 @@
dispatch.burn_item_recursively(from.clone(), token, self_budget, breadth_budget)
}
+ /// Check if token indirectly owned by specified user
+ pub fn check_indirectly_owned(
+ user: T::CrossAccountId,
+ collection: CollectionId,
+ token: TokenId,
+ for_nest: Option<(CollectionId, TokenId)>,
+ budget: &dyn Budget,
+ ) -> Result<bool, DispatchError> {
+ let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
+ Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
+ None => user,
+ };
+
+ Self::get_indirect_owner(
+ collection,
+ token,
+ for_nest,
+ budget
+ ).map(|indirect_owner| indirect_owner == target_parent)
+ }
+
pub fn check_nesting(
from: T::CrossAccountId,
under: &T::CrossAccountId,
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -815,8 +815,9 @@
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+ let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
+ dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount, &budget))
}
/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -147,7 +147,11 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
+
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -169,7 +173,10 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -180,7 +187,11 @@
let owner = match collection.token_owner(nft_id) {
Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
- Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),
+ Some((col, tok)) => {
+ let rmrk_collection = RmrkCore::rmrk_collection_id(col)?;
+
+ RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
+ }
None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
},
None => return Ok(None)
@@ -202,11 +213,11 @@
use pallet_common::CommonCollectionOperations;
let cross_account_id = CrossAccountId::from_sub(account_id);
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
Ok(
collection.account_tokens(cross_account_id)
@@ -217,28 +228,35 @@
}
fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
let nft_id = TokenId(nft_id);
if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
Ok(
pallet_nonfungible::TokenChildren::<Runtime>::iter_prefix((collection_id, nft_id))
- .filter_map(|(child_id, is_child)|
- match is_child {
- true => Some(RmrkNftChild {
- collection_id: child_id.0.0,
- nft_id: child_id.1.0,
- }),
- false => None,
- }
- ).collect()
+ .filter_map(|((child_collection, child_token), _)| {
+ let rmrk_child_collection = RmrkCore::rmrk_collection_id(
+ child_collection
+ ).ok()?;
+
+ Some(RmrkNftChild {
+ collection_id: rmrk_child_collection,
+ nft_id: child_token.0,
+ })
+ }).collect()
)
}
fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
use pallet_proxy_rmrk_core::misc::CollectionType;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() {
return Ok(Vec::new());
}
@@ -259,7 +277,10 @@
fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
use pallet_proxy_rmrk_core::misc::NftType;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
let token_id = TokenId(nft_id);
if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
@@ -283,7 +304,10 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, ResourceType}};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
let nft_id = TokenId(nft_id);
@@ -332,7 +356,10 @@
fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
let nft_id = TokenId(nft_id);
@@ -360,7 +387,10 @@
RmrkProperty, misc::{CollectionType},
};
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -377,12 +407,11 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
-
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
let parts = collection.collection_tokens()
.into_iter()
@@ -413,11 +442,13 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+ return Ok(Vec::new());
+ }
let theme_names = collection.collection_tokens()
@@ -444,11 +475,13 @@
};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+ return Ok(None);
+ }
let theme_info = collection.collection_tokens()
.into_iter()