git.delta.rocks / unique-network / refs/commits / 31bae1fc2671

difftreelog

fix separate depositor from sender in transfer_internal (#1013)

Daniel Shiposha2023-10-16parent: #71be677.patch.diff
in: master
* fix: transfer-from

* fix: remove Option from nester type

* fix: check_nesting comment

* fix: rename nester to depositor, add docs

9 files changed

modifiedpallets/balances-adapter/src/common.rsdiffbeforeafterboth
--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -221,7 +221,7 @@
 
 	fn check_nesting(
 		&self,
-		_sender: <T>::CrossAccountId,
+		_sender: &<T>::CrossAccountId,
 		_from: (up_data_structs::CollectionId, TokenId),
 		_under: TokenId,
 		_budget: &dyn up_data_structs::budget::Budget,
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -2194,7 +2194,7 @@
 	/// * `budget` - The maximum budget that can be spent on the check.
 	fn check_nesting(
 		&self,
-		sender: T::CrossAccountId,
+		sender: &T::CrossAccountId,
 		from: (CollectionId, TokenId),
 		under: TokenId,
 		budget: &dyn Budget,
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -335,7 +335,7 @@
 
 	fn check_nesting(
 		&self,
-		_sender: <T>::CrossAccountId,
+		_sender: &<T>::CrossAccountId,
 		_from: (CollectionId, TokenId),
 		_under: TokenId,
 		_nesting_budget: &dyn Budget,
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
after · pallets/fungible/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//! # Fungible Pallet18//!19//! The Fungible pallet provides functionality for dealing with fungible assets.20//!21//! - [`CreateItemData`]22//! - [`Config`]23//! - [`FungibleHandle`]24//! - [`Pallet`]25//! - [`TotalSupply`]26//! - [`Balance`]27//! - [`Allowance`]28//! - [`Error`]29//!30//! ## Fungible tokens31//!32//! Fungible tokens or assets are divisible and non-unique. For instance,33//! fiat currencies like the dollar are fungible: A $1 bill34//! in New York City has the same value as a $1 bill in Miami.35//! A fungible token can also be a cryptocurrency like Bitcoin: 1 BTC is worth 1 BTC,36//! no matter where it is issued. Thus, the fungibility refers to a specific currency’s37//! ability to maintain one standard value. As well, it needs to have uniform acceptance.38//! This means that a currency’s history should not be able to affect its value,39//! and this is due to the fact that each piece that is a part of the currency is equal40//! in value when compared to every other piece of that exact same currency.41//! In the world of cryptocurrencies, this is essentially a coin or a token42//! that can be replaced by another identical coin or token, and they are43//! both mutually interchangeable. A popular implementation of fungible tokens is44//! the ERC-20 token standard.45//!46//! ### ERC-2047//!48//! The [ERC-20](https://ethereum.org/en/developers/docs/standards/tokens/erc-20/) (Ethereum Request for Comments 20), proposed by Fabian Vogelsteller in November 2015,49//! is a Token Standard that implements an API for tokens within Smart Contracts.50//!51//! Example functionalities ERC-20 provides:52//!53//! * transfer tokens from one account to another54//! * get the current token balance of an account55//! * get the total supply of the token available on the network56//! * approve whether an amount of token from an account can be spent by a third-party account57//!58//! ## Overview59//!60//! The module provides functionality for asset management of fungible asset, supports ERC-20 standart, includes:61//!62//! * Asset Issuance63//! * Asset Transferal64//! * Asset Destruction65//! * Delegated Asset Transfers66//!67//! **NOTE:** The created fungible asset always has `token_id` = 0.68//! So `tokenA` and `tokenB` will have different `collection_id`.69//!70//! ### Implementations71//!72//! The Fungible pallet provides implementations for the following traits.73//!74//! - [`WithRecorder`](pallet_evm_coder_substrate::WithRecorder): Trait for EVM support75//! - [`CommonCollectionOperations`](pallet_common::CommonCollectionOperations): Functions for dealing with collections76//! - [`CommonWeightInfo`](pallet_common::CommonWeightInfo): Functions for retrieval of transaction weight77//! - [`CommonEvmHandler`](pallet_common::erc::CommonEvmHandler): Function for handling EVM runtime calls7879#![cfg_attr(not(feature = "std"), no_std)]8081use core::ops::Deref;8283use evm_coder::ToLog;84use frame_support::{dispatch::PostDispatchInfo, ensure, pallet_prelude::*};85pub use pallet::*;86use pallet_common::{87	eth::collection_id_to_address, helpers::add_weight_to_post_info,88	weights::WeightInfo as CommonWeightInfo, Error as CommonError, Event as CommonEvent,89	Pallet as PalletCommon, SelfWeightOf as PalletCommonWeightOf,90};91use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};92use pallet_evm_coder_substrate::WithRecorder;93use pallet_structure::Pallet as PalletStructure;94use sp_core::H160;95use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};96use sp_std::{collections::btree_map::BTreeMap, vec::Vec};97use up_data_structs::{98	budget::Budget, mapping::TokenAddressMapping, AccessMode, CollectionId, CreateCollectionData,99	Property, PropertyKey, TokenId,100};101use weights::WeightInfo;102103use crate::erc::ERC20Events;104#[cfg(feature = "runtime-benchmarks")]105pub mod benchmarking;106pub mod common;107pub mod erc;108pub mod weights;109110pub type CreateItemData<T> = (<T as pallet_evm::Config>::CrossAccountId, u128);111pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;112113#[frame_support::pallet]114pub mod pallet {115	use frame_support::{116		pallet_prelude::*, storage::Key, Blake2_128, Blake2_128Concat, Twox64Concat,117	};118	use up_data_structs::CollectionId;119120	use super::weights::WeightInfo;121122	#[pallet::error]123	pub enum Error<T> {124		/// Not Fungible item data used to mint in Fungible collection.125		NotFungibleDataUsedToMintFungibleCollectionToken,126		/// Fungible tokens hold no ID, and the default value of TokenId for Fungible collection is 0.127		FungibleItemsHaveNoId,128		/// Tried to set data for fungible item.129		FungibleItemsDontHaveData,130		/// Fungible token does not support nesting.131		FungibleDisallowsNesting,132		/// Setting item properties is not allowed.133		SettingPropertiesNotAllowed,134		/// Setting allowance for all is not allowed.135		SettingAllowanceForAllNotAllowed,136		/// Only a fungible collection could be possibly broken; any fungible token is valid.137		FungibleTokensAreAlwaysValid,138	}139140	#[pallet::config]141	pub trait Config:142		frame_system::Config + pallet_common::Config + pallet_structure::Config + pallet_evm::Config143	{144		type WeightInfo: WeightInfo;145	}146147	#[pallet::pallet]148	pub struct Pallet<T>(_);149150	/// Total amount of fungible tokens inside a collection.151	#[pallet::storage]152	pub type TotalSupply<T: Config> =153		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;154155	/// Amount of tokens owned by an account inside a collection.156	#[pallet::storage]157	pub type Balance<T: Config> = StorageNMap<158		Key = (159			Key<Twox64Concat, CollectionId>,160			Key<Blake2_128Concat, T::CrossAccountId>,161		),162		Value = u128,163		QueryKind = ValueQuery,164	>;165166	/// Storage for assets delegated to a limited extent to other users.167	#[pallet::storage]168	pub type Allowance<T: Config> = StorageNMap<169		Key = (170			Key<Twox64Concat, CollectionId>,171			Key<Blake2_128, T::CrossAccountId>,       // Owner172			Key<Blake2_128Concat, T::CrossAccountId>, // Spender173		),174		Value = u128,175		QueryKind = ValueQuery,176	>;177}178179/// Wrapper around untyped collection handle, asserting inner collection is of fungible type.180/// Required for interaction with Fungible collections, type safety and implementation [`solidity_interface`][`evm_coder::solidity_interface`].181pub struct FungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);182183/// Implementation of methods required for dispatching during runtime.184impl<T: Config> FungibleHandle<T> {185	/// Casts [`CollectionHandle`][`pallet_common::CollectionHandle`] into [`FungibleHandle`].186	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {187		Self(inner)188	}189190	/// Casts [`FungibleHandle`] into [`CollectionHandle`][`pallet_common::CollectionHandle`].191	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {192		self.0193	}194	/// Returns a mutable reference to the internal [`CollectionHandle`][`pallet_common::CollectionHandle`].195	pub fn common_mut(&mut self) -> &mut pallet_common::CollectionHandle<T> {196		&mut self.0197	}198}199impl<T: Config> WithRecorder<T> for FungibleHandle<T> {200	fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {201		self.0.recorder()202	}203	fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {204		self.0.into_recorder()205	}206}207impl<T: Config> Deref for FungibleHandle<T> {208	type Target = pallet_common::CollectionHandle<T>;209210	fn deref(&self) -> &Self::Target {211		&self.0212	}213}214215/// Pallet implementation for fungible assets216impl<T: Config> Pallet<T> {217	/// Initializes the collection. Returns [CollectionId] on success, [DispatchError] otherwise.218	pub fn init_collection(219		owner: T::CrossAccountId,220		payer: T::CrossAccountId,221		data: CreateCollectionData<T::CrossAccountId>,222	) -> Result<CollectionId, DispatchError> {223		<PalletCommon<T>>::init_collection(owner, payer, data)224	}225226	/// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.227	pub fn init_foreign_collection(228		owner: T::CrossAccountId,229		payer: T::CrossAccountId,230		data: CreateCollectionData<T::CrossAccountId>,231	) -> Result<CollectionId, DispatchError> {232		<PalletCommon<T>>::init_foreign_collection(owner, payer, data)233	}234235	/// Destroys a collection.236	pub fn destroy_collection(237		collection: FungibleHandle<T>,238		sender: &T::CrossAccountId,239	) -> DispatchResult {240		let id = collection.id;241242		if Self::collection_has_tokens(id) {243			return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());244		}245246		// =========247248		PalletCommon::destroy_collection(collection.0, sender)?;249250		<TotalSupply<T>>::remove(id);251		let _ = <Balance<T>>::clear_prefix((id,), u32::MAX, None);252		let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);253		Ok(())254	}255256	/// Add properties to the collection.257	pub fn set_collection_properties(258		collection: &FungibleHandle<T>,259		sender: &T::CrossAccountId,260		properties: Vec<Property>,261	) -> DispatchResult {262		<PalletCommon<T>>::set_collection_properties(collection, sender, properties.into_iter())263	}264265	/// Delete properties of the collection, associated with the provided keys.266	pub fn delete_collection_properties(267		collection: &FungibleHandle<T>,268		sender: &T::CrossAccountId,269		property_keys: Vec<PropertyKey>,270	) -> DispatchResult {271		<PalletCommon<T>>::delete_collection_properties(272			collection,273			sender,274			property_keys.into_iter(),275		)276	}277278	/// Checks if collection has tokens. Return `true` if it has.279	fn collection_has_tokens(collection_id: CollectionId) -> bool {280		<TotalSupply<T>>::get(collection_id) != 0281	}282283	/// Burns the specified amount of the token. If the token balance284	/// or total supply is less than the given value,285	/// it will return [DispatchError].286	pub fn burn(287		collection: &FungibleHandle<T>,288		owner: &T::CrossAccountId,289		amount: u128,290	) -> DispatchResult {291		let total_supply = <TotalSupply<T>>::get(collection.id)292			.checked_sub(amount)293			.ok_or(<CommonError<T>>::TokenValueTooLow)?;294295		let balance = <Balance<T>>::get((collection.id, owner))296			.checked_sub(amount)297			.ok_or(<CommonError<T>>::TokenValueTooLow)?;298299		// Foreign collection check300		ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);301302		if collection.permissions.access() == AccessMode::AllowList {303			collection.check_allowlist(owner)?;304		}305306		// =========307308		if balance == 0 {309			<Balance<T>>::remove((collection.id, owner));310			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());311		} else {312			<Balance<T>>::insert((collection.id, owner), balance);313		}314		<TotalSupply<T>>::insert(collection.id, total_supply);315316		<PalletEvm<T>>::deposit_log(317			ERC20Events::Transfer {318				from: *owner.as_eth(),319				to: H160::default(),320				value: amount.into(),321			}322			.to_log(collection_id_to_address(collection.id)),323		);324		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(325			collection.id,326			TokenId::default(),327			owner.clone(),328			amount,329		));330		Ok(())331	}332333	/// Burns the specified amount of the token.334	pub fn burn_foreign(335		collection: &FungibleHandle<T>,336		owner: &T::CrossAccountId,337		amount: u128,338	) -> DispatchResult {339		let total_supply = <TotalSupply<T>>::get(collection.id)340			.checked_sub(amount)341			.ok_or(<CommonError<T>>::TokenValueTooLow)?;342343		let balance = <Balance<T>>::get((collection.id, owner))344			.checked_sub(amount)345			.ok_or(<CommonError<T>>::TokenValueTooLow)?;346		// =========347348		if balance == 0 {349			<Balance<T>>::remove((collection.id, owner));350			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, TokenId::default());351		} else {352			<Balance<T>>::insert((collection.id, owner), balance);353		}354		<TotalSupply<T>>::insert(collection.id, total_supply);355356		<PalletEvm<T>>::deposit_log(357			ERC20Events::Transfer {358				from: *owner.as_eth(),359				to: H160::default(),360				value: amount.into(),361			}362			.to_log(collection_id_to_address(collection.id)),363		);364		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(365			collection.id,366			TokenId::default(),367			owner.clone(),368			amount,369		));370		Ok(())371	}372373	/// Transfers the specified amount of tokens. Will check that374	/// the transfer is allowed for the token.375	///376	/// - `from`: Owner of tokens to transfer.377	/// - `to`: Recepient of transfered tokens.378	/// - `amount`: Amount of tokens to transfer.379	/// - `collection`: Collection that contains the token380	pub fn transfer(381		collection: &FungibleHandle<T>,382		from: &T::CrossAccountId,383		to: &T::CrossAccountId,384		amount: u128,385		nesting_budget: &dyn Budget,386	) -> DispatchResultWithPostInfo {387		let depositor = from;388		Self::transfer_internal(collection, depositor, from, to, amount, nesting_budget)389	}390391	/// Transfers tokens from the `from` account to the `to` account.392	/// The `depositor` is the account who deposits the tokens.393	/// For instance, the nesting rules will be checked against the `depositor`'s permissions.394	fn transfer_internal(395		collection: &FungibleHandle<T>,396		depositor: &T::CrossAccountId,397		from: &T::CrossAccountId,398		to: &T::CrossAccountId,399		amount: u128,400		nesting_budget: &dyn Budget,401	) -> DispatchResultWithPostInfo {402		ensure!(403			collection.limits.transfers_enabled(),404			<CommonError<T>>::TransferNotAllowed,405		);406407		let mut actual_weight = <SelfWeightOf<T>>::transfer_raw();408409		if collection.permissions.access() == AccessMode::AllowList {410			collection.check_allowlist(from)?;411			collection.check_allowlist(to)?;412			actual_weight += <PalletCommonWeightOf<T>>::check_accesslist() * 2;413		}414		<PalletCommon<T>>::ensure_correct_receiver(to)?;415		let balance_from = <Balance<T>>::get((collection.id, from))416			.checked_sub(amount)417			.ok_or(<CommonError<T>>::TokenValueTooLow)?;418		let balance_to = if from != to && amount != 0 {419			Some(420				<Balance<T>>::get((collection.id, to))421					.checked_add(amount)422					.ok_or(ArithmeticError::Overflow)?,423			)424		} else {425			None426		};427428		// =========429430		if let Some(balance_to) = balance_to {431			// from != to && amount != 0432433			<PalletStructure<T>>::nest_if_sent_to_token(434				depositor,435				to,436				collection.id,437				TokenId::default(),438				nesting_budget,439			)?;440441			if balance_from == 0 {442				<Balance<T>>::remove((collection.id, from));443				<PalletStructure<T>>::unnest_if_nested(from, collection.id, TokenId::default());444			} else {445				<Balance<T>>::insert((collection.id, from), balance_from);446			}447			<Balance<T>>::insert((collection.id, to), balance_to);448		}449450		<PalletEvm<T>>::deposit_log(451			ERC20Events::Transfer {452				from: *from.as_eth(),453				to: *to.as_eth(),454				value: amount.into(),455			}456			.to_log(collection_id_to_address(collection.id)),457		);458		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(459			collection.id,460			TokenId::default(),461			from.clone(),462			to.clone(),463			amount,464		));465466		Ok(PostDispatchInfo {467			actual_weight: Some(actual_weight),468			pays_fee: Pays::Yes,469		})470	}471472	/// Minting tokens for multiple IDs.473	/// It is a utility function used in [`create_multiple_items`][`Pallet::create_multiple_items`]474	/// and [`create_multiple_items_foreign`][`Pallet::create_multiple_items_foreign`]475	pub fn create_multiple_items_common(476		collection: &FungibleHandle<T>,477		sender: &T::CrossAccountId,478		data: BTreeMap<T::CrossAccountId, u128>,479		nesting_budget: &dyn Budget,480	) -> DispatchResult {481		let total_supply = data482			.values()483			.copied()484			.try_fold(<TotalSupply<T>>::get(collection.id), |acc, v| {485				acc.checked_add(v)486			})487			.ok_or(ArithmeticError::Overflow)?;488489		for (to, _) in data.iter() {490			<PalletStructure<T>>::check_nesting(491				sender,492				to,493				collection.id,494				TokenId::default(),495				nesting_budget,496			)?;497		}498499		let updated_balances = data500			.into_iter()501			.map(|(user, amount)| {502				let updated_balance = <Balance<T>>::get((collection.id, &user))503					.checked_add(amount)504					.ok_or(ArithmeticError::Overflow)?;505				Ok((user, amount, updated_balance))506			})507			.collect::<Result<Vec<_>, DispatchError>>()?;508509		// =========510511		<TotalSupply<T>>::insert(collection.id, total_supply);512		for (user, amount, updated_balance) in updated_balances {513			<Balance<T>>::insert((collection.id, &user), updated_balance);514			<PalletStructure<T>>::nest_if_sent_to_token_unchecked(515				&user,516				collection.id,517				TokenId::default(),518			);519			<PalletEvm<T>>::deposit_log(520				ERC20Events::Transfer {521					from: H160::default(),522					to: *user.as_eth(),523					value: amount.into(),524				}525				.to_log(collection_id_to_address(collection.id)),526			);527			<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(528				collection.id,529				TokenId::default(),530				user.clone(),531				amount,532			));533		}534535		Ok(())536	}537538	/// Minting tokens for multiple IDs.539	/// See [`create_item`][`Pallet::create_item`] for more details.540	pub fn create_multiple_items(541		collection: &FungibleHandle<T>,542		sender: &T::CrossAccountId,543		data: BTreeMap<T::CrossAccountId, u128>,544		nesting_budget: &dyn Budget,545	) -> DispatchResult {546		// Foreign collection check547		ensure!(!collection.flags.foreign, <CommonError<T>>::NoPermission);548549		if !collection.is_owner_or_admin(sender) {550			ensure!(551				collection.permissions.mint_mode(),552				<CommonError<T>>::PublicMintingNotAllowed553			);554			collection.check_allowlist(sender)?;555556			for (owner, _) in data.iter() {557				collection.check_allowlist(owner)?;558			}559		}560561		Self::create_multiple_items_common(collection, sender, data, nesting_budget)562	}563564	/// Minting tokens for multiple IDs.565	/// See [`create_item_foreign`][`Pallet::create_item_foreign`] for more details.566	pub fn create_multiple_items_foreign(567		collection: &FungibleHandle<T>,568		sender: &T::CrossAccountId,569		data: BTreeMap<T::CrossAccountId, u128>,570		nesting_budget: &dyn Budget,571	) -> DispatchResult {572		Self::create_multiple_items_common(collection, sender, data, nesting_budget)573	}574575	fn set_allowance_unchecked(576		collection: &FungibleHandle<T>,577		owner: &T::CrossAccountId,578		spender: &T::CrossAccountId,579		amount: u128,580	) {581		if amount == 0 {582			<Allowance<T>>::remove((collection.id, owner, spender));583		} else {584			<Allowance<T>>::insert((collection.id, owner, spender), amount);585		}586587		<PalletEvm<T>>::deposit_log(588			ERC20Events::Approval {589				owner: *owner.as_eth(),590				spender: *spender.as_eth(),591				value: amount.into(),592			}593			.to_log(collection_id_to_address(collection.id)),594		);595		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(596			collection.id,597			TokenId(0),598			owner.clone(),599			spender.clone(),600			amount,601		));602	}603604	/// Set allowance for the spender to `transfer` or `burn` owner's tokens.605	///606	/// - `collection`: Collection that contains the token607	/// - `owner`: Owner of tokens that sets the allowance.608	/// - `spender`: Recipient of the allowance rights.609	/// - `amount`: Amount of tokens the spender is allowed to `transfer` or `burn`.610	pub fn set_allowance(611		collection: &FungibleHandle<T>,612		owner: &T::CrossAccountId,613		spender: &T::CrossAccountId,614		amount: u128,615	) -> DispatchResult {616		if collection.permissions.access() == AccessMode::AllowList {617			collection.check_allowlist(owner)?;618			collection.check_allowlist(spender)?;619		}620621		if <Balance<T>>::get((collection.id, owner)) < amount {622			ensure!(623				collection.ignores_owned_amount(owner),624				<CommonError<T>>::CantApproveMoreThanOwned625			);626		}627628		// =========629630		Self::set_allowance_unchecked(collection, owner, spender, amount);631		Ok(())632	}633634	/// Set allowance for the spender to `transfer` or `burn` owner's tokens from eth mirror.635	///636	/// - `collection`: Collection that contains the token637	/// - `sender`: Owner of tokens that sets the allowance.638	/// - `from`: Owner's eth mirror.639	/// - `to`: Recipient of the allowance rights.640	/// - `amount`: Amount of tokens the spender is allowed to `transfer` or `burn`.641	pub fn set_allowance_from(642		collection: &FungibleHandle<T>,643		sender: &T::CrossAccountId,644		from: &T::CrossAccountId,645		to: &T::CrossAccountId,646		amount: u128,647	) -> DispatchResult {648		if collection.permissions.access() == AccessMode::AllowList {649			collection.check_allowlist(sender)?;650			collection.check_allowlist(from)?;651			collection.check_allowlist(to)?;652		}653654		ensure!(655			sender.conv_eq(from),656			<CommonError<T>>::AddressIsNotEthMirror657		);658659		if <Balance<T>>::get((collection.id, from)) < amount {660			ensure!(661				collection.limits.owner_can_transfer()662					&& (collection.is_owner_or_admin(sender) || collection.is_owner_or_admin(from)),663				<CommonError<T>>::CantApproveMoreThanOwned664			);665		}666667		// =========668669		Self::set_allowance_unchecked(collection, from, to, amount);670		Ok(())671	}672673	/// Checks if a non-owner has (enough) allowance from the owner to perform operations on the tokens.674	/// Returns the expected remaining allowance - it should be set manually if the transaction proceeds.675	///676	/// - `collection`: Collection that contains the token.677	/// - `spender`: CrossAccountId who has the allowance rights.678	/// - `from`: The owner of the tokens who sets the allowance.679	/// - `amount`: Amount of tokens by which the allowance sholud be reduced.680	fn check_allowed(681		collection: &FungibleHandle<T>,682		spender: &T::CrossAccountId,683		from: &T::CrossAccountId,684		amount: u128,685		nesting_budget: &dyn Budget,686	) -> Result<Option<u128>, DispatchError> {687		if spender.conv_eq(from) {688			return Ok(None);689		}690		if collection.permissions.access() == AccessMode::AllowList {691			// `from`, `to` checked in [`transfer`]692			collection.check_allowlist(spender)?;693		}694695		if collection.ignores_token_restrictions(spender) {696			return Ok(Self::compute_allowance_decrease(697				collection, from, spender, amount,698			));699		}700701		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {702			ensure!(703				<PalletStructure<T>>::check_indirectly_owned(704					spender.clone(),705					source.0,706					source.1,707					None,708					nesting_budget709				)?,710				<CommonError<T>>::ApprovedValueTooLow,711			);712			return Ok(None);713		}714715		let allowance = Self::compute_allowance_decrease(collection, from, spender, amount);716		ensure!(allowance.is_some(), <CommonError<T>>::ApprovedValueTooLow);717718		Ok(allowance)719	}720721	/// Returns `Some(amount)` if the `spender` have allowance to spend this amount.722	/// Otherwise, it returns `None`.723	fn compute_allowance_decrease(724		collection: &FungibleHandle<T>,725		from: &T::CrossAccountId,726		spender: &T::CrossAccountId,727		amount: u128,728	) -> Option<u128> {729		<Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount)730	}731732	/// Transfer fungible tokens from one account to another.733	/// Same as the [`transfer`][`Pallet::transfer`] but spender doesn't needs to be an owner of the token pieces.734	/// The owner should set allowance for the spender to transfer pieces.735	/// See [`set_allowance`][`Pallet::set_allowance`] for more details.736	pub fn transfer_from(737		collection: &FungibleHandle<T>,738		spender: &T::CrossAccountId,739		from: &T::CrossAccountId,740		to: &T::CrossAccountId,741		amount: u128,742		nesting_budget: &dyn Budget,743	) -> DispatchResultWithPostInfo {744		let allowance = Self::check_allowed(collection, spender, from, amount, nesting_budget)?;745746		// =========747748		let mut result =749			Self::transfer_internal(collection, spender, from, to, amount, nesting_budget);750		add_weight_to_post_info(&mut result, <SelfWeightOf<T>>::check_allowed_raw());751		result?;752753		if let Some(allowance) = allowance {754			Self::set_allowance_unchecked(collection, from, spender, allowance);755			add_weight_to_post_info(756				&mut result,757				<SelfWeightOf<T>>::set_allowance_unchecked_raw(),758			)759		}760		result761	}762763	/// Burn fungible tokens from the account.764	///765	/// Same as the [`burn`][`Pallet::burn`] but spender doesn't need to be an owner of the tokens. The `from` should766	/// set allowance for the spender to burn tokens.767	/// See [`set_allowance`][`Pallet::set_allowance`] for more details.768	pub fn burn_from(769		collection: &FungibleHandle<T>,770		spender: &T::CrossAccountId,771		from: &T::CrossAccountId,772		amount: u128,773		nesting_budget: &dyn Budget,774	) -> DispatchResult {775		let allowance = Self::check_allowed(collection, spender, from, amount, nesting_budget)?;776777		// =========778779		Self::burn(collection, from, amount)?;780		if let Some(allowance) = allowance {781			Self::set_allowance_unchecked(collection, from, spender, allowance);782		}783		Ok(())784	}785786	/// Creates fungible token.787	///788	/// The sender should be the owner/admin of the collection or collection should be configured789	/// to allow public minting.790	///791	/// - `data`: Contains user who will become the owners of the tokens and amount792	///   of tokens he will receive.793	pub fn create_item(794		collection: &FungibleHandle<T>,795		sender: &T::CrossAccountId,796		data: CreateItemData<T>,797		nesting_budget: &dyn Budget,798	) -> DispatchResult {799		Self::create_multiple_items(800			collection,801			sender,802			[(data.0, data.1)].into_iter().collect(),803			nesting_budget,804		)805	}806807	/// Creates fungible token.808	///809	/// - `data`: Contains user who will become the owners of the tokens and amount810	///   of tokens he will receive.811	pub fn create_item_foreign(812		collection: &FungibleHandle<T>,813		sender: &T::CrossAccountId,814		data: CreateItemData<T>,815		nesting_budget: &dyn Budget,816	) -> DispatchResult {817		Self::create_multiple_items_foreign(818			collection,819			sender,820			[(data.0, data.1)].into_iter().collect(),821			nesting_budget,822		)823	}824825	/// Returns 10 tokens owners in no particular order826	///827	/// There is no direct way to get token holders in ascending order,828	/// since `iter_prefix` returns values in no particular order.829	/// Therefore, getting the 10 largest holders with a large value of holders830	/// can lead to impact memory allocation + sorting with  `n * log (n)`.831	pub fn token_owners(832		collection: CollectionId,833		_token: TokenId,834	) -> Option<Vec<T::CrossAccountId>> {835		let res: Vec<T::CrossAccountId> = <Balance<T>>::iter_prefix((collection,))836			.map(|(owner, _amount)| owner)837			.take(10)838			.collect();839840		if res.is_empty() {841			None842		} else {843			Some(res)844		}845	}846}
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -407,7 +407,7 @@
 
 	fn check_nesting(
 		&self,
-		sender: T::CrossAccountId,
+		sender: &T::CrossAccountId,
 		from: (CollectionId, TokenId),
 		under: TokenId,
 		nesting_budget: &dyn Budget,
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -718,6 +718,21 @@
 		token: TokenId,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResultWithPostInfo {
+		let depositor = from;
+		Self::transfer_internal(collection, depositor, from, to, token, nesting_budget)
+	}
+
+	/// Transfers an NFT from the `from` account to the `to` account.
+	/// The `depositor` is the account who deposits the NFT.
+	/// For instance, the nesting rules will be checked against the `depositor`'s permissions.
+	pub fn transfer_internal(
+		collection: &NonfungibleHandle<T>,
+		depositor: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		to: &T::CrossAccountId,
+		token: TokenId,
+		nesting_budget: &dyn Budget,
+	) -> DispatchResultWithPostInfo {
 		ensure!(
 			collection.limits.transfers_enabled(),
 			<CommonError<T>>::TransferNotAllowed
@@ -754,7 +769,7 @@
 		};
 
 		<PalletStructure<T>>::nest_if_sent_to_token(
-			from.clone(),
+			depositor,
 			to,
 			collection.id,
 			token,
@@ -860,7 +875,7 @@
 			let token = TokenId(first_token + i as u32 + 1);
 
 			<PalletStructure<T>>::check_nesting(
-				sender.clone(),
+				sender,
 				&data.owner,
 				collection.id,
 				token,
@@ -1154,7 +1169,8 @@
 		// =========
 
 		// Allowance is reset in [`transfer`]
-		let mut result = Self::transfer(collection, from, to, token, nesting_budget);
+		let mut result =
+			Self::transfer_internal(collection, spender, from, to, token, nesting_budget);
 		add_weight_to_post_info(&mut result, <SelfWeightOf<T>>::check_allowed_raw());
 		result
 	}
@@ -1183,7 +1199,7 @@
 	///
 	pub fn check_nesting(
 		handle: &NonfungibleHandle<T>,
-		sender: T::CrossAccountId,
+		sender: &T::CrossAccountId,
 		from: (CollectionId, TokenId),
 		under: TokenId,
 		nesting_budget: &dyn Budget,
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -416,7 +416,7 @@
 
 	fn check_nesting(
 		&self,
-		_sender: <T>::CrossAccountId,
+		_sender: &<T>::CrossAccountId,
 		_from: (CollectionId, TokenId),
 		_under: TokenId,
 		_nesting_budget: &dyn Budget,
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -615,6 +615,30 @@
 		amount: u128,
 		nesting_budget: &dyn Budget,
 	) -> DispatchResult {
+		let depositor = from;
+		Self::transfer_internal(
+			collection,
+			depositor,
+			from,
+			to,
+			token,
+			amount,
+			nesting_budget,
+		)
+	}
+
+	/// Transfers RFT tokens from the `from` account to the `to` account.
+	/// The `depositor` is the account who deposits the tokens.
+	/// For instance, the nesting rules will be checked against the `depositor`'s permissions.
+	pub fn transfer_internal(
+		collection: &RefungibleHandle<T>,
+		depositor: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		to: &T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+		nesting_budget: &dyn Budget,
+	) -> DispatchResult {
 		ensure!(
 			collection.limits.transfers_enabled(),
 			<CommonError<T>>::TransferNotAllowed
@@ -683,7 +707,7 @@
 			// from != to && amount != 0
 
 			<PalletStructure<T>>::nest_if_sent_to_token(
-				from.clone(),
+				depositor,
 				to,
 				collection.id,
 				token,
@@ -847,7 +871,7 @@
 			let token_id = TokenId(first_token_id + i as u32 + 1);
 			for (to, _) in token.users.iter() {
 				<PalletStructure<T>>::check_nesting(
-					sender.clone(),
+					sender,
 					to,
 					collection.id,
 					token_id,
@@ -1146,7 +1170,7 @@
 
 		// =========
 
-		Self::transfer(collection, from, to, token, amount, nesting_budget)?;
+		Self::transfer_internal(collection, spender, from, to, token, amount, nesting_budget)?;
 		if let Some(allowance) = allowance {
 			Self::set_allowance_unchecked(collection, from, spender, token, allowance);
 		}
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -300,7 +300,7 @@
 	///
 	/// - `nesting_budget`: Limit for searching parents in depth.
 	pub fn check_nesting(
-		from: T::CrossAccountId,
+		from: &T::CrossAccountId,
 		under: &T::CrossAccountId,
 		collection_id: CollectionId,
 		token_id: TokenId,
@@ -317,7 +317,7 @@
 	///
 	/// - `nesting_budget`: Limit for searching parents in depth.
 	pub fn nest_if_sent_to_token(
-		from: T::CrossAccountId,
+		from: &T::CrossAccountId,
 		under: &T::CrossAccountId,
 		collection_id: CollectionId,
 		token_id: TokenId,