git.delta.rocks / unique-network / refs/commits / 7e808da4a3d9

difftreelog

fix revert set_allowance

Daniel Shiposha2022-06-05parent: #e099d8f.patch.diff
in: master

8 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1298,7 +1298,6 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
-		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo;
 	fn transfer_from(
 		&self,
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -226,7 +226,6 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
-		_nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
 		ensure!(
 			token == TokenId::default(),
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -308,15 +308,14 @@
 		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), nesting_budget)
+				<Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))
 			} else {
-				<Pallet<T>>::set_allowance(self, &sender, token, None, nesting_budget)
+				<Pallet<T>>::set_allowance(self, &sender, token, None)
 			},
 			<CommonWeights<T>>::approve(),
 		)
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -269,11 +269,8 @@
 		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), &budget)
+		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
 			.map_err(dispatch_to_evm::<T>)?;
 		Ok(())
 	}
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -878,7 +878,6 @@
 		sender: &T::CrossAccountId,
 		token: TokenId,
 		spender: Option<&T::CrossAccountId>,
-		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
 		if collection.permissions.access() == AccessMode::AllowList {
 			collection.check_allowlist(sender)?;
@@ -890,16 +889,10 @@
 		if let Some(spender) = spender {
 			<PalletCommon<T>>::ensure_correct_receiver(spender)?;
 		}
-
-		let is_owned = <PalletStructure<T>>::check_indirectly_owned(
-			sender.clone(),
-			collection.id,
-			token,
-			None,
-			nesting_budget
-		)?;
 
-		if !is_owned {
+		let token_data =
+			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
+		if &token_data.owner != sender {
 			ensure!(
 				collection.ignores_owned_amount(sender),
 				<CommonError<T>>::CantApproveMoreThanOwned
@@ -926,9 +919,6 @@
 			// `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!(
@@ -943,6 +933,9 @@
 			);
 			return Ok(());
 		}
+		if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
+			return Ok(());
+		}
 		ensure!(
 			collection.ignores_allowance(spender),
 			<CommonError<T>>::ApprovedValueTooLow
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
before · pallets/proxy-rmrk-core/src/lib.rs
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(&resource_collection, &sender, resource_id)796				.map_err(Self::map_common_err_to_proxy)?;797		} else {798			<PalletNft<T>>::set_scoped_token_property(799				resource_collection_id,800				resource_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_typed_nft_collection_mapped(916		rmrk_collection_id: RmrkCollectionId,917		collection_type: misc::CollectionType,918	) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {919		let unique_collection_id = Self::unique_collection_id(rmrk_collection_id)?;920921		let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;922923		Ok((collection, unique_collection_id))924	}925926	pub fn get_nft_property(927		collection_id: CollectionId,928		nft_id: TokenId,929		key: RmrkProperty,930	) -> Result<PropertyValue, DispatchError> {931		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))932			.get(&Self::rmrk_property_key(key)?)933			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?934			.clone();935936		Ok(nft_property)937	}938939	pub fn get_nft_property_decoded<V: Decode>(940		collection_id: CollectionId,941		nft_id: TokenId,942		key: RmrkProperty,943	) -> Result<V, DispatchError> {944		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)945	}946947	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {948		<TokenData<T>>::contains_key((collection_id, nft_id))949	}950951	pub fn get_nft_type(952		collection_id: CollectionId,953		token_id: TokenId,954	) -> Result<NftType, DispatchError> {955		Self::get_nft_property_decoded(collection_id, token_id, TokenType)956			.map_err(|_| <Error<T>>::NftTypeEncodeError.into())957	}958959	pub fn ensure_nft_type(960		collection_id: CollectionId,961		token_id: TokenId,962		nft_type: NftType,963	) -> DispatchResult {964		let actual_type = Self::get_nft_type(collection_id, token_id)?;965		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);966967		Ok(())968	}969970	pub fn ensure_nft_owner(971		collection_id: CollectionId,972		token_id: TokenId,973		possible_owner: &T::CrossAccountId,974		nesting_budget: &dyn budget::Budget975	) -> DispatchResult {976		let is_owned = <PalletStructure<T>>::check_indirectly_owned(977			possible_owner.clone(),978			collection_id,979			token_id,980			None,981			nesting_budget,982		)?;983984		ensure!(985			is_owned,986			<Error<T>>::NoPermission987		);988989		Ok(())990	}991992	pub fn filter_user_properties<Key, Value, R, Mapper>(993		collection_id: CollectionId,994		token_id: Option<TokenId>,995		filter_keys: Option<Vec<RmrkPropertyKey>>,996		mapper: Mapper,997	) -> Result<Vec<R>, DispatchError>998	where999		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1000		Value: Decode + Default,1001		Mapper: Fn(Key, Value) -> R,1002	{1003		filter_keys1004			.map(|keys| {1005				let properties = keys1006					.into_iter()1007					.filter_map(|key| {1008						let key: Key = key.try_into().ok()?;10091010						let value = match token_id {1011							Some(token_id) => Self::get_nft_property_decoded(1012								collection_id,1013								token_id,1014								UserProperty(key.as_ref()),1015							),1016							None => Self::get_collection_property_decoded(1017								collection_id,1018								UserProperty(key.as_ref()),1019							),1020						}1021						.ok()?;10221023						Some(mapper(key, value))1024					})1025					.collect();10261027				Ok(properties)1028			})1029			.unwrap_or_else(|| {1030				let properties =1031					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();10321033				Ok(properties)1034			})1035	}10361037	pub fn iterate_user_properties<Key, Value, R, Mapper>(1038		collection_id: CollectionId,1039		token_id: Option<TokenId>,1040		mapper: Mapper,1041	) -> Result<impl Iterator<Item = R>, DispatchError>1042	where1043		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1044		Value: Decode + Default,1045		Mapper: Fn(Key, Value) -> R,1046	{1047		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;10481049		let properties = match token_id {1050			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1051			None => <PalletCommon<T>>::collection_properties(collection_id),1052		};10531054		let properties = properties.into_iter().filter_map(move |(key, value)| {1055			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;10561057			let key: Key = key.to_vec().try_into().ok()?;1058			let value: Value = value.decode().ok()?;10591060			Some(mapper(key, value))1061		});10621063		Ok(properties)1064	}10651066	fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {1067		map_common_err_to_proxy! {1068			match err {1069				NoPermission => NoPermission,1070				CollectionTokenLimitExceeded => CollectionFullOrLocked,1071				PublicMintingNotAllowed => NoPermission,1072				TokenNotFound => NoAvailableNftId,1073				ApprovedValueTooLow => NoPermission1074			}1075		}1076	}1077}
after · pallets/proxy-rmrk-core/src/lib.rs
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						// FIXME418						// <PalletNft<T>>::set_allowance(419						// 	&collection,420						// 	&cross_sender,421						// 	nft_id,422						// 	Some(&spender),423						// 	&budget424						// ).map_err(Self::map_common_err_to_proxy)?;425426						return Ok(());427					}428				}429			}430431			<PalletNft<T>>::transfer_from(432				&collection,433				&cross_sender,434				&from,435				&target_owner,436				nft_id,437				&budget438			).map_err(Self::map_common_err_to_proxy)?;439440			Ok(())441		}442443		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]444		#[transactional]445		pub fn set_property(446			origin: OriginFor<T>,447			#[pallet::compact] rmrk_collection_id: RmrkCollectionId,448			maybe_nft_id: Option<RmrkNftId>,449			key: RmrkKeyString,450			value: RmrkValueString,451		) -> DispatchResult {452			let sender = ensure_signed(origin)?;453			let sender = T::CrossAccountId::from_sub(sender);454455			let collection_id = Self::unique_collection_id(rmrk_collection_id)?;456			let budget = budget::Value::new(NESTING_BUDGET);457458			match maybe_nft_id {459				Some(nft_id) => {460					let token_id: TokenId = nft_id.into();461462					Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;463					Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;464465					<PalletNft<T>>::set_scoped_token_property(466						collection_id,467						token_id,468						PropertyScope::Rmrk,469						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,470					)?;471				}472				None => {473					let collection = Self::get_typed_nft_collection(474						collection_id,475						misc::CollectionType::Regular,476					)?;477478					Self::check_collection_owner(&collection, &sender)?;479480					<PalletCommon<T>>::set_scoped_collection_property(481						collection_id,482						PropertyScope::Rmrk,483						Self::rmrk_property(UserProperty(key.as_slice()), &value)?,484					)?;485				}486			}487488			Self::deposit_event(Event::PropertySet {489				collection_id: rmrk_collection_id,490				maybe_nft_id,491				key,492				value,493			});494495			Ok(())496		}497498		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]499		#[transactional]500		pub fn add_basic_resource(501			origin: OriginFor<T>,502			collection_id: RmrkCollectionId,503			nft_id: RmrkNftId,504			resource: RmrkBasicResource,505		) -> DispatchResult {506			let sender = ensure_signed(origin.clone())?;507508			let resource_id = Self::resource_add(509				sender,510				Self::unique_collection_id(collection_id)?,511				nft_id.into(),512				[513					Self::rmrk_property(TokenType, &NftType::Resource)?,514					Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,515					Self::rmrk_property(Src, &resource.src)?,516					Self::rmrk_property(Metadata, &resource.metadata)?,517					Self::rmrk_property(License, &resource.license)?,518					Self::rmrk_property(Thumb, &resource.thumb)?,519				]520				.into_iter(),521			)?;522523			Self::deposit_event(Event::ResourceAdded {524				nft_id,525				resource_id,526			});527			Ok(())528		}529530		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]531		#[transactional]532		pub fn add_composable_resource(533			origin: OriginFor<T>,534			collection_id: RmrkCollectionId,535			nft_id: RmrkNftId,536			_resource_id: RmrkBoundedResource,537			resource: RmrkComposableResource,538		) -> DispatchResult {539			let sender = ensure_signed(origin.clone())?;540541			let resource_id = Self::resource_add(542				sender,543				Self::unique_collection_id(collection_id)?,544				nft_id.into(),545				[546					Self::rmrk_property(TokenType, &NftType::Resource)?,547					Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,548					Self::rmrk_property(Parts, &resource.parts)?,549					Self::rmrk_property(Base, &resource.base)?,550					Self::rmrk_property(Src, &resource.src)?,551					Self::rmrk_property(Metadata, &resource.metadata)?,552					Self::rmrk_property(License, &resource.license)?,553					Self::rmrk_property(Thumb, &resource.thumb)?,554				]555				.into_iter(),556			)?;557558			Self::deposit_event(Event::ResourceAdded {559				nft_id,560				resource_id,561			});562			Ok(())563		}564565		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]566		#[transactional]567		pub fn add_slot_resource(568			origin: OriginFor<T>,569			collection_id: RmrkCollectionId,570			nft_id: RmrkNftId,571			resource: RmrkSlotResource,572		) -> DispatchResult {573			let sender = ensure_signed(origin.clone())?;574575			let resource_id = Self::resource_add(576				sender,577				Self::unique_collection_id(collection_id)?,578				nft_id.into(),579				[580					Self::rmrk_property(TokenType, &NftType::Resource)?,581					Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,582					Self::rmrk_property(Base, &resource.base)?,583					Self::rmrk_property(Src, &resource.src)?,584					Self::rmrk_property(Metadata, &resource.metadata)?,585					Self::rmrk_property(Slot, &resource.slot)?,586					Self::rmrk_property(License, &resource.license)?,587					Self::rmrk_property(Thumb, &resource.thumb)?,588				]589				.into_iter(),590			)?;591592			Self::deposit_event(Event::ResourceAdded {593				nft_id,594				resource_id,595			});596			Ok(())597		}598599		#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]600		#[transactional]601		pub fn remove_resource(602			origin: OriginFor<T>,603			collection_id: RmrkCollectionId,604			nft_id: RmrkNftId,605			resource_id: RmrkResourceId,606		) -> DispatchResult {607			let sender = ensure_signed(origin.clone())?;608609			Self::resource_remove(610				sender,611				Self::unique_collection_id(collection_id)?,612				nft_id.into(),613				resource_id.into(),614			)?;615616			Self::deposit_event(Event::ResourceRemoval {617				nft_id,618				resource_id,619			});620			Ok(())621		}622	}623}624625impl<T: Config> Pallet<T> {626	pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {627		let key = rmrk_key.to_key::<T>()?;628629		let scoped_key = PropertyScope::Rmrk630			.apply(key)631			.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;632633		Ok(scoped_key)634	}635636	// todo think about renaming these637	pub fn rmrk_property<E: Encode>(638		rmrk_key: RmrkProperty,639		value: &E,640	) -> Result<Property, DispatchError> {641		let key = rmrk_key.to_key::<T>()?;642643		let value = value644			.encode()645			.try_into()646			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;647648		let property = Property { key, value };649650		Ok(property)651	}652653	pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {654		vec.decode()655			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())656	}657658	pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>659	where660		BoundedVec<u8, S>: TryFrom<Vec<u8>>,661	{662		vec.rebind()663			.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())664	}665666	fn init_collection(667		sender: T::CrossAccountId,668		data: CreateCollectionData<T::AccountId>,669		properties: impl Iterator<Item = Property>,670	) -> Result<CollectionId, DispatchError> {671		let collection_id = <PalletNft<T>>::init_collection(sender, data);672673		if let Err(DispatchError::Arithmetic(_)) = &collection_id {674			return Err(<Error<T>>::NoAvailableCollectionId.into());675		}676677		<PalletCommon<T>>::set_scoped_collection_properties(678			collection_id?,679			PropertyScope::Rmrk,680			properties,681		)?;682683		collection_id684	}685686	pub fn create_nft(687		sender: &T::CrossAccountId,688		owner: &T::CrossAccountId,689		collection: &NonfungibleHandle<T>,690		properties: impl Iterator<Item = Property>,691	) -> Result<TokenId, DispatchError> {692		let data = CreateNftExData {693			properties: BoundedVec::default(),694			owner: owner.clone(),695		};696697		let budget = budget::Value::new(NESTING_BUDGET);698699		<PalletNft<T>>::create_item(collection, sender, data, &budget)?;700701		let nft_id = <PalletNft<T>>::current_token_id(collection.id);702703		<PalletNft<T>>::set_scoped_token_properties(704			collection.id,705			nft_id,706			PropertyScope::Rmrk,707			properties,708		)?;709710		Ok(nft_id)711	}712713	fn destroy_nft(714		sender: T::CrossAccountId,715		collection_id: CollectionId,716		collection_type: misc::CollectionType,717		token_id: TokenId,718	) -> DispatchResult {719		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;720721		<PalletNft<T>>::burn(&collection, &sender, token_id)722			.map_err(Self::map_common_err_to_proxy)?;723724		Ok(())725	}726727	fn resource_add(728		sender: T::AccountId,729		collection_id: CollectionId,730		token_id: TokenId,731		resource_properties: impl Iterator<Item = Property>,732	) -> Result<RmrkResourceId, DispatchError> {733		let collection =734			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;735		ensure!(collection.owner == sender, Error::<T>::NoPermission);736737		// Check NFT lock status // todo depends on market, maybe later738		//ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);739740		let sender = T::CrossAccountId::from_sub(sender);741		let budget = budget::Value::new(NESTING_BUDGET);742		let pending = Self::ensure_nft_owner(collection_id, token_id, &sender, &budget).is_err();743744		let resource_collection_id: CollectionId =745			Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;746		let resource_collection =747			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;748749		// todo probably add extra connections to bases, slots, etc., when RMRK starts to use them750751		let resource_id = Self::create_nft(752			&sender, // todo owner of the nft?753			&sender,754			&resource_collection,755			resource_properties.chain(756				[757					Self::rmrk_property(PendingResourceAccept, &pending)?,758					Self::rmrk_property(PendingResourceRemoval, &false)?,759				]760				.into_iter(),761			),762		)763		.map_err(|err| match err {764			DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),765			err => Self::map_common_err_to_proxy(err),766		})?;767768		Ok(resource_id.0)769	}770771	fn resource_remove(772		sender: T::AccountId,773		collection_id: CollectionId,774		nft_id: TokenId,775		resource_id: TokenId,776	) -> DispatchResult {777		let collection =778			Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;779		ensure!(collection.owner == sender, Error::<T>::NoPermission);780781		let resource_collection_id: CollectionId =782			Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;783		let resource_collection =784			Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;785		ensure!(786			<PalletNft<T>>::token_exists(&resource_collection, resource_id),787			Error::<T>::ResourceDoesntExist788		);789790		let budget = up_data_structs::budget::Value::new(10);791		let topmost_owner =792			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;793794		let sender = T::CrossAccountId::from_sub(sender);795		if topmost_owner == sender {796			<PalletNft<T>>::burn(&resource_collection, &sender, resource_id)797				.map_err(Self::map_common_err_to_proxy)?;798		} else {799			<PalletNft<T>>::set_scoped_token_property(800				resource_collection_id,801				resource_id,802				PropertyScope::Rmrk,803				Self::rmrk_property(PendingResourceRemoval, &true)?,804			)?;805		}806807		Ok(())808	}809810	fn change_collection_owner(811		collection_id: CollectionId,812		collection_type: misc::CollectionType,813		sender: T::AccountId,814		new_owner: T::AccountId,815	) -> DispatchResult {816		let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;817		Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;818819		let mut collection = collection.into_inner();820821		collection.owner = new_owner;822		collection.save()823	}824825	fn check_collection_owner(826		collection: &NonfungibleHandle<T>,827		account: &T::CrossAccountId,828	) -> DispatchResult {829		collection830			.check_is_owner(account)831			.map_err(Self::map_common_err_to_proxy)832	}833834	pub fn last_collection_idx() -> RmrkCollectionId {835		<CollectionIndex<T>>::get()836	}837838	pub fn unique_collection_id(839		rmrk_collection_id: RmrkCollectionId,840	) -> Result<CollectionId, DispatchError> {841		<UniqueCollectionId<T>>::try_get(rmrk_collection_id)842			.map_err(|_| <Error<T>>::CollectionUnknown.into())843	}844845	pub fn rmrk_collection_id(846		unique_collection_id: CollectionId847	) -> Result<RmrkCollectionId, DispatchError> {848		<RmrkInernalCollectionId<T>>::try_get(unique_collection_id)849			.map_err(|_| <Error<T>>::CollectionUnknown.into())850	}851852	pub fn get_nft_collection(853		collection_id: CollectionId,854	) -> Result<NonfungibleHandle<T>, DispatchError> {855		let collection = <CollectionHandle<T>>::try_get(collection_id)856			.map_err(|_| <Error<T>>::CollectionUnknown)?;857858		match collection.mode {859			CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),860			_ => Err(<Error<T>>::CollectionUnknown.into()),861		}862	}863864	pub fn collection_exists(collection_id: CollectionId) -> bool {865		<CollectionHandle<T>>::try_get(collection_id).is_ok()866	}867868	pub fn get_collection_property(869		collection_id: CollectionId,870		key: RmrkProperty,871	) -> Result<PropertyValue, DispatchError> {872		let collection_property = <PalletCommon<T>>::collection_properties(collection_id)873			.get(&Self::rmrk_property_key(key)?)874			.ok_or(<Error<T>>::CollectionUnknown)?875			.clone();876877		Ok(collection_property)878	}879880	pub fn get_collection_property_decoded<V: Decode>(881		collection_id: CollectionId,882		key: RmrkProperty,883	) -> Result<V, DispatchError> {884		Self::decode_property(Self::get_collection_property(collection_id, key)?)885	}886887	pub fn get_collection_type(888		collection_id: CollectionId,889	) -> Result<misc::CollectionType, DispatchError> {890		Self::get_collection_property_decoded(collection_id, CollectionType)891			.map_err(|_| <Error<T>>::CorruptedCollectionType.into())892	}893894	pub fn ensure_collection_type(895		collection_id: CollectionId,896		collection_type: misc::CollectionType,897	) -> DispatchResult {898		let actual_type = Self::get_collection_type(collection_id)?;899		ensure!(900			actual_type == collection_type,901			<CommonError<T>>::NoPermission902		);903904		Ok(())905	}906907	pub fn get_typed_nft_collection(908		collection_id: CollectionId,909		collection_type: misc::CollectionType,910	) -> Result<NonfungibleHandle<T>, DispatchError> {911		Self::ensure_collection_type(collection_id, collection_type)?;912913		Self::get_nft_collection(collection_id)914	}915916	pub fn get_typed_nft_collection_mapped(917		rmrk_collection_id: RmrkCollectionId,918		collection_type: misc::CollectionType,919	) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {920		let unique_collection_id = Self::unique_collection_id(rmrk_collection_id)?;921922		let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;923924		Ok((collection, unique_collection_id))925	}926927	pub fn get_nft_property(928		collection_id: CollectionId,929		nft_id: TokenId,930		key: RmrkProperty,931	) -> Result<PropertyValue, DispatchError> {932		let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))933			.get(&Self::rmrk_property_key(key)?)934			.ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?935			.clone();936937		Ok(nft_property)938	}939940	pub fn get_nft_property_decoded<V: Decode>(941		collection_id: CollectionId,942		nft_id: TokenId,943		key: RmrkProperty,944	) -> Result<V, DispatchError> {945		Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)946	}947948	pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {949		<TokenData<T>>::contains_key((collection_id, nft_id))950	}951952	pub fn get_nft_type(953		collection_id: CollectionId,954		token_id: TokenId,955	) -> Result<NftType, DispatchError> {956		Self::get_nft_property_decoded(collection_id, token_id, TokenType)957			.map_err(|_| <Error<T>>::NftTypeEncodeError.into())958	}959960	pub fn ensure_nft_type(961		collection_id: CollectionId,962		token_id: TokenId,963		nft_type: NftType,964	) -> DispatchResult {965		let actual_type = Self::get_nft_type(collection_id, token_id)?;966		ensure!(actual_type == nft_type, <Error<T>>::NoPermission);967968		Ok(())969	}970971	pub fn ensure_nft_owner(972		collection_id: CollectionId,973		token_id: TokenId,974		possible_owner: &T::CrossAccountId,975		nesting_budget: &dyn budget::Budget976	) -> DispatchResult {977		let is_owned = <PalletStructure<T>>::check_indirectly_owned(978			possible_owner.clone(),979			collection_id,980			token_id,981			None,982			nesting_budget,983		)?;984985		ensure!(986			is_owned,987			<Error<T>>::NoPermission988		);989990		Ok(())991	}992993	pub fn filter_user_properties<Key, Value, R, Mapper>(994		collection_id: CollectionId,995		token_id: Option<TokenId>,996		filter_keys: Option<Vec<RmrkPropertyKey>>,997		mapper: Mapper,998	) -> Result<Vec<R>, DispatchError>999	where1000		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1001		Value: Decode + Default,1002		Mapper: Fn(Key, Value) -> R,1003	{1004		filter_keys1005			.map(|keys| {1006				let properties = keys1007					.into_iter()1008					.filter_map(|key| {1009						let key: Key = key.try_into().ok()?;10101011						let value = match token_id {1012							Some(token_id) => Self::get_nft_property_decoded(1013								collection_id,1014								token_id,1015								UserProperty(key.as_ref()),1016							),1017							None => Self::get_collection_property_decoded(1018								collection_id,1019								UserProperty(key.as_ref()),1020							),1021						}1022						.ok()?;10231024						Some(mapper(key, value))1025					})1026					.collect();10271028				Ok(properties)1029			})1030			.unwrap_or_else(|| {1031				let properties =1032					Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();10331034				Ok(properties)1035			})1036	}10371038	pub fn iterate_user_properties<Key, Value, R, Mapper>(1039		collection_id: CollectionId,1040		token_id: Option<TokenId>,1041		mapper: Mapper,1042	) -> Result<impl Iterator<Item = R>, DispatchError>1043	where1044		Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1045		Value: Decode + Default,1046		Mapper: Fn(Key, Value) -> R,1047	{1048		let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;10491050		let properties = match token_id {1051			Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1052			None => <PalletCommon<T>>::collection_properties(collection_id),1053		};10541055		let properties = properties.into_iter().filter_map(move |(key, value)| {1056			let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;10571058			let key: Key = key.to_vec().try_into().ok()?;1059			let value: Value = value.decode().ok()?;10601061			Some(mapper(key, value))1062		});10631064		Ok(properties)1065	}10661067	fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {1068		map_common_err_to_proxy! {1069			match err {1070				NoPermission => NoPermission,1071				CollectionTokenLimitExceeded => CollectionFullOrLocked,1072				PublicMintingNotAllowed => NoPermission,1073				TokenNotFound => NoAvailableNftId,1074				ApprovedValueTooLow => NoPermission1075			}1076		}1077	}1078}
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -254,7 +254,6 @@
 		spender: T::CrossAccountId,
 		token: TokenId,
 		amount: u128,
-		_nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -815,9 +815,8 @@
 		#[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, &budget))
+			dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
 		}
 
 		/// 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.