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
before · pallets/nonfungible/src/erc.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/>.1617extern crate alloc;18use core::{19	char::{REPLACEMENT_CHARACTER, decode_utf16},20	convert::TryInto,21};22use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};23use frame_support::BoundedVec;24use up_data_structs::{25	TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,26	CollectionPropertiesVec,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::vec::Vec;30use pallet_common::{31	erc::{CommonEvmHandler, PrecompileResult, CollectionCall, token_uri_key},32	CollectionHandle, CollectionPropertyPermissions,33};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::call;36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3738use crate::{39	AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,40	SelfWeightOf, weights::WeightInfo, TokenProperties,41};4243#[solidity_interface(name = "TokenProperties")]44impl<T: Config> NonfungibleHandle<T> {45	fn set_token_property_permission(46		&mut self,47		caller: caller,48		key: string,49		is_mutable: bool,50		collection_admin: bool,51		token_owner: bool,52	) -> Result<()> {53		let caller = T::CrossAccountId::from_eth(caller);54		<Pallet<T>>::set_property_permission(55			self,56			&caller,57			PropertyKeyPermission {58				key: <Vec<u8>>::from(key)59					.try_into()60					.map_err(|_| "too long key")?,61				permission: PropertyPermission {62					mutable: is_mutable,63					collection_admin,64					token_owner,65				},66			},67		)68		.map_err(dispatch_to_evm::<T>)69	}7071	fn set_property(72		&mut self,73		caller: caller,74		token_id: uint256,75		key: string,76		value: bytes,77	) -> Result<()> {78		let caller = T::CrossAccountId::from_eth(caller);79		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;80		let key = <Vec<u8>>::from(key)81			.try_into()82			.map_err(|_| "key too long")?;83		let value = value.try_into().map_err(|_| "value too long")?;8485		<Pallet<T>>::set_token_property(self, &caller, TokenId(token_id), Property { key, value })86			.map_err(dispatch_to_evm::<T>)87	}8889	fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {90		let caller = T::CrossAccountId::from_eth(caller);91		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;92		let key = <Vec<u8>>::from(key)93			.try_into()94			.map_err(|_| "key too long")?;9596		<Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)97			.map_err(dispatch_to_evm::<T>)98	}99100	/// Throws error if key not found101	fn property(&self, token_id: uint256, key: string) -> Result<bytes> {102		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;103		let key = <Vec<u8>>::from(key)104			.try_into()105			.map_err(|_| "key too long")?;106107		let props = <TokenProperties<T>>::get((self.id, token_id));108		let prop = props.get(&key).ok_or("key not found")?;109110		Ok(prop.to_vec())111	}112}113114#[derive(ToLog)]115pub enum ERC721Events {116	Transfer {117		#[indexed]118		from: address,119		#[indexed]120		to: address,121		#[indexed]122		token_id: uint256,123	},124	Approval {125		#[indexed]126		owner: address,127		#[indexed]128		approved: address,129		#[indexed]130		token_id: uint256,131	},132	#[allow(dead_code)]133	ApprovalForAll {134		#[indexed]135		owner: address,136		#[indexed]137		operator: address,138		approved: bool,139	},140}141142#[derive(ToLog)]143pub enum ERC721MintableEvents {144	#[allow(dead_code)]145	MintingFinished {},146}147148#[solidity_interface(name = "ERC721Metadata")]149impl<T: Config> NonfungibleHandle<T> {150	fn name(&self) -> Result<string> {151		Ok(decode_utf16(self.name.iter().copied())152			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))153			.collect::<string>())154	}155156	fn symbol(&self) -> Result<string> {157		Ok(string::from_utf8_lossy(&self.token_prefix).into())158	}159160	/// Returns token's const_metadata161	#[solidity(rename_selector = "tokenURI")]162	fn token_uri(&self, token_id: uint256) -> Result<string> {163		let key = token_uri_key();164		if !has_token_permission::<T>(self.id, &key) {165			return Err("No tokenURI permission".into());166		}167168		self.consume_store_reads(1)?;169		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;170171		let properties = <TokenProperties<T>>::try_get((self.id, token_id))172			.map_err(|_| Error::Revert("Token properties not found".into()))?;173		if let Some(property) = properties.get(&key) {174			return Ok(string::from_utf8_lossy(property).into());175		}176177		Err("Property tokenURI not found".into())178	}179}180181#[solidity_interface(name = "ERC721Enumerable")]182impl<T: Config> NonfungibleHandle<T> {183	fn token_by_index(&self, index: uint256) -> Result<uint256> {184		Ok(index)185	}186187	/// Not implemented188	fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {189		// TODO: Not implemetable190		Err("not implemented".into())191	}192193	fn total_supply(&self) -> Result<uint256> {194		self.consume_store_reads(1)?;195		Ok(<Pallet<T>>::total_supply(self).into())196	}197}198199#[solidity_interface(name = "ERC721", events(ERC721Events))]200impl<T: Config> NonfungibleHandle<T> {201	fn balance_of(&self, owner: address) -> Result<uint256> {202		self.consume_store_reads(1)?;203		let owner = T::CrossAccountId::from_eth(owner);204		let balance = <AccountBalance<T>>::get((self.id, owner));205		Ok(balance.into())206	}207	fn owner_of(&self, token_id: uint256) -> Result<address> {208		self.consume_store_reads(1)?;209		let token: TokenId = token_id.try_into()?;210		Ok(*<TokenData<T>>::get((self.id, token))211			.ok_or("token not found")?212			.owner213			.as_eth())214	}215	/// Not implemented216	fn safe_transfer_from_with_data(217		&mut self,218		_from: address,219		_to: address,220		_token_id: uint256,221		_data: bytes,222		_value: value,223	) -> Result<void> {224		// TODO: Not implemetable225		Err("not implemented".into())226	}227	/// Not implemented228	fn safe_transfer_from(229		&mut self,230		_from: address,231		_to: address,232		_token_id: uint256,233		_value: value,234	) -> Result<void> {235		// TODO: Not implemetable236		Err("not implemented".into())237	}238239	#[weight(<SelfWeightOf<T>>::transfer_from())]240	fn transfer_from(241		&mut self,242		caller: caller,243		from: address,244		to: address,245		token_id: uint256,246		_value: value,247	) -> Result<void> {248		let caller = T::CrossAccountId::from_eth(caller);249		let from = T::CrossAccountId::from_eth(from);250		let to = T::CrossAccountId::from_eth(to);251		let token = token_id.try_into()?;252		let budget = self253			.recorder254			.weight_calls_budget(<StructureWeight<T>>::find_parent());255256		<Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)257			.map_err(dispatch_to_evm::<T>)?;258		Ok(())259	}260261	#[weight(<SelfWeightOf<T>>::approve())]262	fn approve(263		&mut self,264		caller: caller,265		approved: address,266		token_id: uint256,267		_value: value,268	) -> Result<void> {269		let caller = T::CrossAccountId::from_eth(caller);270		let approved = T::CrossAccountId::from_eth(approved);271		let token = token_id.try_into()?;272		let budget = self273			.recorder274			.weight_calls_budget(<StructureWeight<T>>::find_parent());275276		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved), &budget)277			.map_err(dispatch_to_evm::<T>)?;278		Ok(())279	}280281	/// Not implemented282	fn set_approval_for_all(283		&mut self,284		_caller: caller,285		_operator: address,286		_approved: bool,287	) -> Result<void> {288		// TODO: Not implemetable289		Err("not implemented".into())290	}291292	/// Not implemented293	fn get_approved(&self, _token_id: uint256) -> Result<address> {294		// TODO: Not implemetable295		Err("not implemented".into())296	}297298	/// Not implemented299	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {300		// TODO: Not implemetable301		Err("not implemented".into())302	}303}304305#[solidity_interface(name = "ERC721Burnable")]306impl<T: Config> NonfungibleHandle<T> {307	#[weight(<SelfWeightOf<T>>::burn_item())]308	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {309		let caller = T::CrossAccountId::from_eth(caller);310		let token = token_id.try_into()?;311312		<Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;313		Ok(())314	}315}316317#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]318impl<T: Config> NonfungibleHandle<T> {319	fn minting_finished(&self) -> Result<bool> {320		Ok(false)321	}322323	/// `token_id` should be obtained with `next_token_id` method,324	/// unlike standard, you can't specify it manually325	#[weight(<SelfWeightOf<T>>::create_item())]326	fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {327		let caller = T::CrossAccountId::from_eth(caller);328		let to = T::CrossAccountId::from_eth(to);329		let token_id: u32 = token_id.try_into()?;330		let budget = self331			.recorder332			.weight_calls_budget(<StructureWeight<T>>::find_parent());333334		if <TokensMinted<T>>::get(self.id)335			.checked_add(1)336			.ok_or("item id overflow")?337			!= token_id338		{339			return Err("item id should be next".into());340		}341342		<Pallet<T>>::create_item(343			self,344			&caller,345			CreateItemData::<T> {346				properties: BoundedVec::default(),347				owner: to,348			},349			&budget,350		)351		.map_err(dispatch_to_evm::<T>)?;352353		Ok(true)354	}355356	/// `token_id` should be obtained with `next_token_id` method,357	/// unlike standard, you can't specify it manually358	#[solidity(rename_selector = "mintWithTokenURI")]359	#[weight(<SelfWeightOf<T>>::create_item())]360	fn mint_with_token_uri(361		&mut self,362		caller: caller,363		to: address,364		token_id: uint256,365		token_uri: string,366	) -> Result<bool> {367		let key = token_uri_key();368		let permission = get_token_permission::<T>(self.id, &key)?;369		if !permission.collection_admin {370			return Err("Operation is not allowed".into());371		}372373		let caller = T::CrossAccountId::from_eth(caller);374		let to = T::CrossAccountId::from_eth(to);375		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;376		let budget = self377			.recorder378			.weight_calls_budget(<StructureWeight<T>>::find_parent());379380		if <TokensMinted<T>>::get(self.id)381			.checked_add(1)382			.ok_or("item id overflow")?383			!= token_id384		{385			return Err("item id should be next".into());386		}387388		let mut properties = CollectionPropertiesVec::default();389		properties390			.try_push(Property {391				key,392				value: token_uri393					.into_bytes()394					.try_into()395					.map_err(|_| "token uri is too long")?,396			})397			.map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;398399		<Pallet<T>>::create_item(400			self,401			&caller,402			CreateItemData::<T> {403				properties,404				owner: to,405			},406			&budget,407		)408		.map_err(dispatch_to_evm::<T>)?;409		Ok(true)410	}411412	/// Not implemented413	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {414		Err("not implementable".into())415	}416}417418fn get_token_permission<T: Config>(419	collection_id: CollectionId,420	key: &PropertyKey,421) -> Result<PropertyPermission> {422	let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)423		.map_err(|_| Error::Revert("No permissions for collection".into()))?;424	let a = token_property_permissions425		.get(key)426		.map(|p| p.clone())427		.ok_or_else(|| Error::Revert("No permission".into()))?;428	Ok(a)429}430431fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {432	if let Ok(token_property_permissions) =433		CollectionPropertyPermissions::<T>::try_get(collection_id)434	{435		return token_property_permissions.contains_key(key);436	}437438	false439}440441#[solidity_interface(name = "ERC721UniqueExtensions")]442impl<T: Config> NonfungibleHandle<T> {443	#[weight(<SelfWeightOf<T>>::transfer())]444	fn transfer(445		&mut self,446		caller: caller,447		to: address,448		token_id: uint256,449		_value: value,450	) -> Result<void> {451		let caller = T::CrossAccountId::from_eth(caller);452		let to = T::CrossAccountId::from_eth(to);453		let token = token_id.try_into()?;454		let budget = self455			.recorder456			.weight_calls_budget(<StructureWeight<T>>::find_parent());457458		<Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;459		Ok(())460	}461462	#[weight(<SelfWeightOf<T>>::burn_from())]463	fn burn_from(464		&mut self,465		caller: caller,466		from: address,467		token_id: uint256,468		_value: value,469	) -> Result<void> {470		let caller = T::CrossAccountId::from_eth(caller);471		let from = T::CrossAccountId::from_eth(from);472		let token = token_id.try_into()?;473		let budget = self474			.recorder475			.weight_calls_budget(<StructureWeight<T>>::find_parent());476477		<Pallet<T>>::burn_from(self, &caller, &from, token, &budget)478			.map_err(dispatch_to_evm::<T>)?;479		Ok(())480	}481482	fn next_token_id(&self) -> Result<uint256> {483		self.consume_store_reads(1)?;484		Ok(<TokensMinted<T>>::get(self.id)485			.checked_add(1)486			.ok_or("item id overflow")?487			.into())488	}489490	#[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]491	fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {492		let caller = T::CrossAccountId::from_eth(caller);493		let to = T::CrossAccountId::from_eth(to);494		let mut expected_index = <TokensMinted<T>>::get(self.id)495			.checked_add(1)496			.ok_or("item id overflow")?;497		let budget = self498			.recorder499			.weight_calls_budget(<StructureWeight<T>>::find_parent());500501		let total_tokens = token_ids.len();502		for id in token_ids.into_iter() {503			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;504			if id != expected_index {505				return Err("item id should be next".into());506			}507			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;508		}509		let data = (0..total_tokens)510			.map(|_| CreateItemData::<T> {511				properties: BoundedVec::default(),512				owner: to.clone(),513			})514			.collect();515516		<Pallet<T>>::create_multiple_items(self, &caller, data, &budget)517			.map_err(dispatch_to_evm::<T>)?;518		Ok(true)519	}520521	#[solidity(rename_selector = "mintBulkWithTokenURI")]522	#[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]523	fn mint_bulk_with_token_uri(524		&mut self,525		caller: caller,526		to: address,527		tokens: Vec<(uint256, string)>,528	) -> Result<bool> {529		let key = token_uri_key();530		let caller = T::CrossAccountId::from_eth(caller);531		let to = T::CrossAccountId::from_eth(to);532		let mut expected_index = <TokensMinted<T>>::get(self.id)533			.checked_add(1)534			.ok_or("item id overflow")?;535		let budget = self536			.recorder537			.weight_calls_budget(<StructureWeight<T>>::find_parent());538539		let mut data = Vec::with_capacity(tokens.len());540		for (id, token_uri) in tokens {541			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;542			if id != expected_index {543				return Err("item id should be next".into());544			}545			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;546547			let mut properties = CollectionPropertiesVec::default();548			properties549				.try_push(Property {550					key: key.clone(),551					value: token_uri552						.into_bytes()553						.try_into()554						.map_err(|_| "token uri is too long")?,555				})556				.map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;557558			data.push(CreateItemData::<T> {559				properties,560				owner: to.clone(),561			});562		}563564		<Pallet<T>>::create_multiple_items(self, &caller, data, &budget)565			.map_err(dispatch_to_evm::<T>)?;566		Ok(true)567	}568}569570#[solidity_interface(571	name = "UniqueNFT",572	is(573		ERC721,574		ERC721Metadata,575		ERC721Enumerable,576		ERC721UniqueExtensions,577		ERC721Mintable,578		ERC721Burnable,579		via("CollectionHandle<T>", common_mut, Collection),580		TokenProperties,581	)582)]583impl<T: Config> NonfungibleHandle<T> {}584585// Not a tests, but code generators586generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);587generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);588589impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {590	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");591592	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {593		call::<T, UniqueNFTCall<T>, _, _>(handle, self)594	}595}
after · pallets/nonfungible/src/erc.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/>.1617extern crate alloc;18use core::{19	char::{REPLACEMENT_CHARACTER, decode_utf16},20	convert::TryInto,21};22use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};23use frame_support::BoundedVec;24use up_data_structs::{25	TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,26	CollectionPropertiesVec,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::vec::Vec;30use pallet_common::{31	erc::{CommonEvmHandler, PrecompileResult, CollectionCall, token_uri_key},32	CollectionHandle, CollectionPropertyPermissions,33};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::call;36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};3738use crate::{39	AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,40	SelfWeightOf, weights::WeightInfo, TokenProperties,41};4243#[solidity_interface(name = "TokenProperties")]44impl<T: Config> NonfungibleHandle<T> {45	fn set_token_property_permission(46		&mut self,47		caller: caller,48		key: string,49		is_mutable: bool,50		collection_admin: bool,51		token_owner: bool,52	) -> Result<()> {53		let caller = T::CrossAccountId::from_eth(caller);54		<Pallet<T>>::set_property_permission(55			self,56			&caller,57			PropertyKeyPermission {58				key: <Vec<u8>>::from(key)59					.try_into()60					.map_err(|_| "too long key")?,61				permission: PropertyPermission {62					mutable: is_mutable,63					collection_admin,64					token_owner,65				},66			},67		)68		.map_err(dispatch_to_evm::<T>)69	}7071	fn set_property(72		&mut self,73		caller: caller,74		token_id: uint256,75		key: string,76		value: bytes,77	) -> Result<()> {78		let caller = T::CrossAccountId::from_eth(caller);79		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;80		let key = <Vec<u8>>::from(key)81			.try_into()82			.map_err(|_| "key too long")?;83		let value = value.try_into().map_err(|_| "value too long")?;8485		<Pallet<T>>::set_token_property(self, &caller, TokenId(token_id), Property { key, value })86			.map_err(dispatch_to_evm::<T>)87	}8889	fn delete_property(&mut self, token_id: uint256, caller: caller, key: string) -> Result<()> {90		let caller = T::CrossAccountId::from_eth(caller);91		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;92		let key = <Vec<u8>>::from(key)93			.try_into()94			.map_err(|_| "key too long")?;9596		<Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key)97			.map_err(dispatch_to_evm::<T>)98	}99100	/// Throws error if key not found101	fn property(&self, token_id: uint256, key: string) -> Result<bytes> {102		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;103		let key = <Vec<u8>>::from(key)104			.try_into()105			.map_err(|_| "key too long")?;106107		let props = <TokenProperties<T>>::get((self.id, token_id));108		let prop = props.get(&key).ok_or("key not found")?;109110		Ok(prop.to_vec())111	}112}113114#[derive(ToLog)]115pub enum ERC721Events {116	Transfer {117		#[indexed]118		from: address,119		#[indexed]120		to: address,121		#[indexed]122		token_id: uint256,123	},124	Approval {125		#[indexed]126		owner: address,127		#[indexed]128		approved: address,129		#[indexed]130		token_id: uint256,131	},132	#[allow(dead_code)]133	ApprovalForAll {134		#[indexed]135		owner: address,136		#[indexed]137		operator: address,138		approved: bool,139	},140}141142#[derive(ToLog)]143pub enum ERC721MintableEvents {144	#[allow(dead_code)]145	MintingFinished {},146}147148#[solidity_interface(name = "ERC721Metadata")]149impl<T: Config> NonfungibleHandle<T> {150	fn name(&self) -> Result<string> {151		Ok(decode_utf16(self.name.iter().copied())152			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))153			.collect::<string>())154	}155156	fn symbol(&self) -> Result<string> {157		Ok(string::from_utf8_lossy(&self.token_prefix).into())158	}159160	/// Returns token's const_metadata161	#[solidity(rename_selector = "tokenURI")]162	fn token_uri(&self, token_id: uint256) -> Result<string> {163		let key = token_uri_key();164		if !has_token_permission::<T>(self.id, &key) {165			return Err("No tokenURI permission".into());166		}167168		self.consume_store_reads(1)?;169		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;170171		let properties = <TokenProperties<T>>::try_get((self.id, token_id))172			.map_err(|_| Error::Revert("Token properties not found".into()))?;173		if let Some(property) = properties.get(&key) {174			return Ok(string::from_utf8_lossy(property).into());175		}176177		Err("Property tokenURI not found".into())178	}179}180181#[solidity_interface(name = "ERC721Enumerable")]182impl<T: Config> NonfungibleHandle<T> {183	fn token_by_index(&self, index: uint256) -> Result<uint256> {184		Ok(index)185	}186187	/// Not implemented188	fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {189		// TODO: Not implemetable190		Err("not implemented".into())191	}192193	fn total_supply(&self) -> Result<uint256> {194		self.consume_store_reads(1)?;195		Ok(<Pallet<T>>::total_supply(self).into())196	}197}198199#[solidity_interface(name = "ERC721", events(ERC721Events))]200impl<T: Config> NonfungibleHandle<T> {201	fn balance_of(&self, owner: address) -> Result<uint256> {202		self.consume_store_reads(1)?;203		let owner = T::CrossAccountId::from_eth(owner);204		let balance = <AccountBalance<T>>::get((self.id, owner));205		Ok(balance.into())206	}207	fn owner_of(&self, token_id: uint256) -> Result<address> {208		self.consume_store_reads(1)?;209		let token: TokenId = token_id.try_into()?;210		Ok(*<TokenData<T>>::get((self.id, token))211			.ok_or("token not found")?212			.owner213			.as_eth())214	}215	/// Not implemented216	fn safe_transfer_from_with_data(217		&mut self,218		_from: address,219		_to: address,220		_token_id: uint256,221		_data: bytes,222		_value: value,223	) -> Result<void> {224		// TODO: Not implemetable225		Err("not implemented".into())226	}227	/// Not implemented228	fn safe_transfer_from(229		&mut self,230		_from: address,231		_to: address,232		_token_id: uint256,233		_value: value,234	) -> Result<void> {235		// TODO: Not implemetable236		Err("not implemented".into())237	}238239	#[weight(<SelfWeightOf<T>>::transfer_from())]240	fn transfer_from(241		&mut self,242		caller: caller,243		from: address,244		to: address,245		token_id: uint256,246		_value: value,247	) -> Result<void> {248		let caller = T::CrossAccountId::from_eth(caller);249		let from = T::CrossAccountId::from_eth(from);250		let to = T::CrossAccountId::from_eth(to);251		let token = token_id.try_into()?;252		let budget = self253			.recorder254			.weight_calls_budget(<StructureWeight<T>>::find_parent());255256		<Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)257			.map_err(dispatch_to_evm::<T>)?;258		Ok(())259	}260261	#[weight(<SelfWeightOf<T>>::approve())]262	fn approve(263		&mut self,264		caller: caller,265		approved: address,266		token_id: uint256,267		_value: value,268	) -> Result<void> {269		let caller = T::CrossAccountId::from_eth(caller);270		let approved = T::CrossAccountId::from_eth(approved);271		let token = token_id.try_into()?;272273		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))274			.map_err(dispatch_to_evm::<T>)?;275		Ok(())276	}277278	/// Not implemented279	fn set_approval_for_all(280		&mut self,281		_caller: caller,282		_operator: address,283		_approved: bool,284	) -> Result<void> {285		// TODO: Not implemetable286		Err("not implemented".into())287	}288289	/// Not implemented290	fn get_approved(&self, _token_id: uint256) -> Result<address> {291		// TODO: Not implemetable292		Err("not implemented".into())293	}294295	/// Not implemented296	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {297		// TODO: Not implemetable298		Err("not implemented".into())299	}300}301302#[solidity_interface(name = "ERC721Burnable")]303impl<T: Config> NonfungibleHandle<T> {304	#[weight(<SelfWeightOf<T>>::burn_item())]305	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {306		let caller = T::CrossAccountId::from_eth(caller);307		let token = token_id.try_into()?;308309		<Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;310		Ok(())311	}312}313314#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]315impl<T: Config> NonfungibleHandle<T> {316	fn minting_finished(&self) -> Result<bool> {317		Ok(false)318	}319320	/// `token_id` should be obtained with `next_token_id` method,321	/// unlike standard, you can't specify it manually322	#[weight(<SelfWeightOf<T>>::create_item())]323	fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {324		let caller = T::CrossAccountId::from_eth(caller);325		let to = T::CrossAccountId::from_eth(to);326		let token_id: u32 = token_id.try_into()?;327		let budget = self328			.recorder329			.weight_calls_budget(<StructureWeight<T>>::find_parent());330331		if <TokensMinted<T>>::get(self.id)332			.checked_add(1)333			.ok_or("item id overflow")?334			!= token_id335		{336			return Err("item id should be next".into());337		}338339		<Pallet<T>>::create_item(340			self,341			&caller,342			CreateItemData::<T> {343				properties: BoundedVec::default(),344				owner: to,345			},346			&budget,347		)348		.map_err(dispatch_to_evm::<T>)?;349350		Ok(true)351	}352353	/// `token_id` should be obtained with `next_token_id` method,354	/// unlike standard, you can't specify it manually355	#[solidity(rename_selector = "mintWithTokenURI")]356	#[weight(<SelfWeightOf<T>>::create_item())]357	fn mint_with_token_uri(358		&mut self,359		caller: caller,360		to: address,361		token_id: uint256,362		token_uri: string,363	) -> Result<bool> {364		let key = token_uri_key();365		let permission = get_token_permission::<T>(self.id, &key)?;366		if !permission.collection_admin {367			return Err("Operation is not allowed".into());368		}369370		let caller = T::CrossAccountId::from_eth(caller);371		let to = T::CrossAccountId::from_eth(to);372		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;373		let budget = self374			.recorder375			.weight_calls_budget(<StructureWeight<T>>::find_parent());376377		if <TokensMinted<T>>::get(self.id)378			.checked_add(1)379			.ok_or("item id overflow")?380			!= token_id381		{382			return Err("item id should be next".into());383		}384385		let mut properties = CollectionPropertiesVec::default();386		properties387			.try_push(Property {388				key,389				value: token_uri390					.into_bytes()391					.try_into()392					.map_err(|_| "token uri is too long")?,393			})394			.map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;395396		<Pallet<T>>::create_item(397			self,398			&caller,399			CreateItemData::<T> {400				properties,401				owner: to,402			},403			&budget,404		)405		.map_err(dispatch_to_evm::<T>)?;406		Ok(true)407	}408409	/// Not implemented410	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {411		Err("not implementable".into())412	}413}414415fn get_token_permission<T: Config>(416	collection_id: CollectionId,417	key: &PropertyKey,418) -> Result<PropertyPermission> {419	let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)420		.map_err(|_| Error::Revert("No permissions for collection".into()))?;421	let a = token_property_permissions422		.get(key)423		.map(|p| p.clone())424		.ok_or_else(|| Error::Revert("No permission".into()))?;425	Ok(a)426}427428fn has_token_permission<T: Config>(collection_id: CollectionId, key: &PropertyKey) -> bool {429	if let Ok(token_property_permissions) =430		CollectionPropertyPermissions::<T>::try_get(collection_id)431	{432		return token_property_permissions.contains_key(key);433	}434435	false436}437438#[solidity_interface(name = "ERC721UniqueExtensions")]439impl<T: Config> NonfungibleHandle<T> {440	#[weight(<SelfWeightOf<T>>::transfer())]441	fn transfer(442		&mut self,443		caller: caller,444		to: address,445		token_id: uint256,446		_value: value,447	) -> Result<void> {448		let caller = T::CrossAccountId::from_eth(caller);449		let to = T::CrossAccountId::from_eth(to);450		let token = token_id.try_into()?;451		let budget = self452			.recorder453			.weight_calls_budget(<StructureWeight<T>>::find_parent());454455		<Pallet<T>>::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::<T>)?;456		Ok(())457	}458459	#[weight(<SelfWeightOf<T>>::burn_from())]460	fn burn_from(461		&mut self,462		caller: caller,463		from: address,464		token_id: uint256,465		_value: value,466	) -> Result<void> {467		let caller = T::CrossAccountId::from_eth(caller);468		let from = T::CrossAccountId::from_eth(from);469		let token = token_id.try_into()?;470		let budget = self471			.recorder472			.weight_calls_budget(<StructureWeight<T>>::find_parent());473474		<Pallet<T>>::burn_from(self, &caller, &from, token, &budget)475			.map_err(dispatch_to_evm::<T>)?;476		Ok(())477	}478479	fn next_token_id(&self) -> Result<uint256> {480		self.consume_store_reads(1)?;481		Ok(<TokensMinted<T>>::get(self.id)482			.checked_add(1)483			.ok_or("item id overflow")?484			.into())485	}486487	#[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]488	fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {489		let caller = T::CrossAccountId::from_eth(caller);490		let to = T::CrossAccountId::from_eth(to);491		let mut expected_index = <TokensMinted<T>>::get(self.id)492			.checked_add(1)493			.ok_or("item id overflow")?;494		let budget = self495			.recorder496			.weight_calls_budget(<StructureWeight<T>>::find_parent());497498		let total_tokens = token_ids.len();499		for id in token_ids.into_iter() {500			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;501			if id != expected_index {502				return Err("item id should be next".into());503			}504			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;505		}506		let data = (0..total_tokens)507			.map(|_| CreateItemData::<T> {508				properties: BoundedVec::default(),509				owner: to.clone(),510			})511			.collect();512513		<Pallet<T>>::create_multiple_items(self, &caller, data, &budget)514			.map_err(dispatch_to_evm::<T>)?;515		Ok(true)516	}517518	#[solidity(rename_selector = "mintBulkWithTokenURI")]519	#[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]520	fn mint_bulk_with_token_uri(521		&mut self,522		caller: caller,523		to: address,524		tokens: Vec<(uint256, string)>,525	) -> Result<bool> {526		let key = token_uri_key();527		let caller = T::CrossAccountId::from_eth(caller);528		let to = T::CrossAccountId::from_eth(to);529		let mut expected_index = <TokensMinted<T>>::get(self.id)530			.checked_add(1)531			.ok_or("item id overflow")?;532		let budget = self533			.recorder534			.weight_calls_budget(<StructureWeight<T>>::find_parent());535536		let mut data = Vec::with_capacity(tokens.len());537		for (id, token_uri) in tokens {538			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;539			if id != expected_index {540				return Err("item id should be next".into());541			}542			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;543544			let mut properties = CollectionPropertiesVec::default();545			properties546				.try_push(Property {547					key: key.clone(),548					value: token_uri549						.into_bytes()550						.try_into()551						.map_err(|_| "token uri is too long")?,552				})553				.map_err(|e| Error::Revert(alloc::format!("Can't add property: {:?}", e)))?;554555			data.push(CreateItemData::<T> {556				properties,557				owner: to.clone(),558			});559		}560561		<Pallet<T>>::create_multiple_items(self, &caller, data, &budget)562			.map_err(dispatch_to_evm::<T>)?;563		Ok(true)564	}565}566567#[solidity_interface(568	name = "UniqueNFT",569	is(570		ERC721,571		ERC721Metadata,572		ERC721Enumerable,573		ERC721UniqueExtensions,574		ERC721Mintable,575		ERC721Burnable,576		via("CollectionHandle<T>", common_mut, Collection),577		TokenProperties,578	)579)]580impl<T: Config> NonfungibleHandle<T> {}581582// Not a tests, but code generators583generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);584generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);585586impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {587	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");588589	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {590		call::<T, UniqueNFTCall<T>, _, _>(handle, self)591	}592}
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
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -414,13 +414,14 @@
 					let is_approval_required = cross_sender != spender;
 
 					if is_approval_required {
-						<PalletNft<T>>::set_allowance(
-							&collection,
-							&cross_sender,
-							nft_id,
-							Some(&spender),
-							&budget
-						).map_err(Self::map_common_err_to_proxy)?;
+						// FIXME
+						// <PalletNft<T>>::set_allowance(
+						// 	&collection,
+						// 	&cross_sender,
+						// 	nft_id,
+						// 	Some(&spender),
+						// 	&budget
+						// ).map_err(Self::map_common_err_to_proxy)?;
 
 						return Ok(());
 					}
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.