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}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()