git.delta.rocks / unique-network / refs/commits / e0035410299d

difftreelog

fix find_parent

Daniel Shiposha2023-01-19parent: #030bc0d.patch.diff
in: master

8 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -155,6 +155,11 @@
 }
 
 impl<T: Config> CollectionHandle<T> {
+	/// Get the mode of the collection: NFT/FT/RFT.
+	pub fn mode(&self) -> CollectionMode {
+		self.mode
+	}
+
 	/// Same as [CollectionHandle::new] but with an explicit gas limit.
 	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
 		<CollectionById<T>>::get(id).map(|collection| Self {
@@ -1872,6 +1877,9 @@
 /// It wraps methods in Fungible, Nonfungible and Refungible pallets
 /// and adds weight info.
 pub trait CommonCollectionOperations<T: Config> {
+	/// Get the mode of the collection: NFT/FT/RFT.
+	fn mode(&self) -> CollectionMode;
+
 	/// Create token.
 	///
 	/// * `sender` - The user who mint the token and pays for the transaction.
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
before · pallets/fungible/src/common.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/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};20use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};21use pallet_common::{22	CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,23	weights::WeightInfo as _,24};25use pallet_structure::Error as StructureError;26use sp_runtime::ArithmeticError;27use sp_std::{vec::Vec, vec};28use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};2930use crate::{31	Allowance, TotalSupply, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf,32	weights::WeightInfo,33};3435pub struct CommonWeights<T: Config>(PhantomData<T>);36impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {37	fn create_item() -> Weight {38		<SelfWeightOf<T>>::create_item()39	}4041	fn create_multiple_items(_data: &[CreateItemData]) -> Weight {42		// All items minted for the same user, so it works same as create_item43		Self::create_item()44	}4546	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {47		match data {48			CreateItemExData::Fungible(f) => {49				<SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)50			}51			_ => Weight::zero(),52		}53	}5455	fn burn_item() -> Weight {56		<SelfWeightOf<T>>::burn_item()57	}5859	fn set_collection_properties(amount: u32) -> Weight {60		<pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)61	}6263	fn delete_collection_properties(amount: u32) -> Weight {64		<pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)65	}6667	fn set_token_properties(_amount: u32) -> Weight {68		// Error69		Weight::zero()70	}7172	fn delete_token_properties(_amount: u32) -> Weight {73		// Error74		Weight::zero()75	}7677	fn set_token_property_permissions(_amount: u32) -> Weight {78		// Error79		Weight::zero()80	}8182	fn transfer() -> Weight {83		<SelfWeightOf<T>>::transfer()84	}8586	fn approve() -> Weight {87		<SelfWeightOf<T>>::approve()88	}8990	fn approve_from() -> Weight {91		<SelfWeightOf<T>>::approve_from()92	}9394	fn transfer_from() -> Weight {95		<SelfWeightOf<T>>::transfer_from()96	}9798	fn burn_from() -> Weight {99		<SelfWeightOf<T>>::burn_from()100	}101102	fn burn_recursively_self_raw() -> Weight {103		// Read to get total balance104		Self::burn_item() + T::DbWeight::get().reads(1)105	}106107	fn burn_recursively_breadth_raw(_amount: u32) -> Weight {108		// Fungible tokens can't have children109		Weight::zero()110	}111112	fn token_owner() -> Weight {113		Weight::zero()114	}115116	fn set_allowance_for_all() -> Weight {117		Weight::zero()118	}119120	fn force_repair_item() -> Weight {121		Weight::zero()122	}123}124125/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete126/// methods and adds weight info.127impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {128	fn create_item(129		&self,130		sender: T::CrossAccountId,131		to: T::CrossAccountId,132		data: up_data_structs::CreateItemData,133		nesting_budget: &dyn Budget,134	) -> DispatchResultWithPostInfo {135		match data {136			up_data_structs::CreateItemData::Fungible(data) => with_weight(137				<Pallet<T>>::create_item(self, &sender, (to, data.value), nesting_budget),138				<CommonWeights<T>>::create_item(),139			),140			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),141		}142	}143144	fn create_multiple_items(145		&self,146		sender: T::CrossAccountId,147		to: T::CrossAccountId,148		data: Vec<up_data_structs::CreateItemData>,149		nesting_budget: &dyn Budget,150	) -> DispatchResultWithPostInfo {151		let mut sum: u128 = 0;152		for data in data {153			match data {154				up_data_structs::CreateItemData::Fungible(data) => {155					sum = sum156						.checked_add(data.value)157						.ok_or(ArithmeticError::Overflow)?;158				}159				_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),160			}161		}162163		with_weight(164			<Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),165			<CommonWeights<T>>::create_item(),166		)167	}168169	fn create_multiple_items_ex(170		&self,171		sender: <T>::CrossAccountId,172		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,173		nesting_budget: &dyn Budget,174	) -> DispatchResultWithPostInfo {175		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);176		let data = match data {177			up_data_structs::CreateItemExData::Fungible(f) => f,178			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),179		};180181		with_weight(182			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),183			weight,184		)185	}186187	fn burn_item(188		&self,189		sender: T::CrossAccountId,190		token: TokenId,191		amount: u128,192	) -> DispatchResultWithPostInfo {193		ensure!(194			token == TokenId::default(),195			<Error<T>>::FungibleItemsHaveNoId196		);197198		with_weight(199			<Pallet<T>>::burn(self, &sender, amount),200			<CommonWeights<T>>::burn_item(),201		)202	}203204	fn burn_item_recursively(205		&self,206		sender: T::CrossAccountId,207		token: TokenId,208		self_budget: &dyn Budget,209		_breadth_budget: &dyn Budget,210	) -> DispatchResultWithPostInfo {211		// Should not happen?212		ensure!(213			token == TokenId::default(),214			<Error<T>>::FungibleItemsHaveNoId215		);216		ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);217218		with_weight(219			<Pallet<T>>::burn(self, &sender, <Balance<T>>::get((self.id, &sender))),220			<CommonWeights<T>>::burn_recursively_self_raw(),221		)222	}223224	fn transfer(225		&self,226		from: T::CrossAccountId,227		to: T::CrossAccountId,228		token: TokenId,229		amount: u128,230		nesting_budget: &dyn Budget,231	) -> DispatchResultWithPostInfo {232		ensure!(233			token == TokenId::default(),234			<Error<T>>::FungibleItemsHaveNoId235		);236237		with_weight(238			<Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget),239			<CommonWeights<T>>::transfer(),240		)241	}242243	fn approve(244		&self,245		sender: T::CrossAccountId,246		spender: T::CrossAccountId,247		token: TokenId,248		amount: u128,249	) -> DispatchResultWithPostInfo {250		ensure!(251			token == TokenId::default(),252			<Error<T>>::FungibleItemsHaveNoId253		);254255		with_weight(256			<Pallet<T>>::set_allowance(self, &sender, &spender, amount),257			<CommonWeights<T>>::approve(),258		)259	}260261	fn approve_from(262		&self,263		sender: T::CrossAccountId,264		from: T::CrossAccountId,265		to: T::CrossAccountId,266		token: TokenId,267		amount: u128,268	) -> DispatchResultWithPostInfo {269		ensure!(270			token == TokenId::default(),271			<Error<T>>::FungibleItemsHaveNoId272		);273274		with_weight(275			<Pallet<T>>::set_allowance_from(self, &sender, &from, &to, amount),276			<CommonWeights<T>>::approve_from(),277		)278	}279280	fn transfer_from(281		&self,282		sender: T::CrossAccountId,283		from: T::CrossAccountId,284		to: T::CrossAccountId,285		token: TokenId,286		amount: u128,287		nesting_budget: &dyn Budget,288	) -> DispatchResultWithPostInfo {289		ensure!(290			token == TokenId::default(),291			<Error<T>>::FungibleItemsHaveNoId292		);293294		with_weight(295			<Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget),296			<CommonWeights<T>>::transfer_from(),297		)298	}299300	fn burn_from(301		&self,302		sender: T::CrossAccountId,303		from: T::CrossAccountId,304		token: TokenId,305		amount: u128,306		nesting_budget: &dyn Budget,307	) -> DispatchResultWithPostInfo {308		ensure!(309			token == TokenId::default(),310			<Error<T>>::FungibleItemsHaveNoId311		);312313		with_weight(314			<Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),315			<CommonWeights<T>>::burn_from(),316		)317	}318319	fn set_collection_properties(320		&self,321		sender: T::CrossAccountId,322		properties: Vec<Property>,323	) -> DispatchResultWithPostInfo {324		let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);325326		with_weight(327			<Pallet<T>>::set_collection_properties(self, &sender, properties),328			weight,329		)330	}331332	fn delete_collection_properties(333		&self,334		sender: &T::CrossAccountId,335		property_keys: Vec<PropertyKey>,336	) -> DispatchResultWithPostInfo {337		let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);338339		with_weight(340			<Pallet<T>>::delete_collection_properties(self, sender, property_keys),341			weight,342		)343	}344345	fn set_token_properties(346		&self,347		_sender: T::CrossAccountId,348		_token_id: TokenId,349		_property: Vec<Property>,350		_nesting_budget: &dyn Budget,351	) -> DispatchResultWithPostInfo {352		fail!(<Error<T>>::SettingPropertiesNotAllowed)353	}354355	fn set_token_property_permissions(356		&self,357		_sender: &T::CrossAccountId,358		_property_permissions: Vec<PropertyKeyPermission>,359	) -> DispatchResultWithPostInfo {360		fail!(<Error<T>>::SettingPropertiesNotAllowed)361	}362363	fn delete_token_properties(364		&self,365		_sender: T::CrossAccountId,366		_token_id: TokenId,367		_property_keys: Vec<PropertyKey>,368		_nesting_budget: &dyn Budget,369	) -> DispatchResultWithPostInfo {370		fail!(<Error<T>>::SettingPropertiesNotAllowed)371	}372373	fn check_nesting(374		&self,375		_sender: <T>::CrossAccountId,376		_from: (CollectionId, TokenId),377		_under: TokenId,378		_nesting_budget: &dyn Budget,379	) -> sp_runtime::DispatchResult {380		fail!(<Error<T>>::FungibleDisallowsNesting)381	}382383	fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}384385	fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}386387	fn collection_tokens(&self) -> Vec<TokenId> {388		vec![TokenId::default()]389	}390391	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {392		if <Balance<T>>::get((self.id, account)) != 0 {393			vec![TokenId::default()]394		} else {395			vec![]396		}397	}398399	fn token_exists(&self, token: TokenId) -> bool {400		token == TokenId::default()401	}402403	fn last_token_id(&self) -> TokenId {404		TokenId::default()405	}406407	fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {408		None409	}410411	/// Returns 10 tokens owners in no particular order.412	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {413		<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()414	}415416	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {417		None418	}419420	fn token_properties(421		&self,422		_token_id: TokenId,423		_keys: Option<Vec<PropertyKey>>,424	) -> Vec<Property> {425		Vec::new()426	}427428	fn total_supply(&self) -> u32 {429		1430	}431432	fn account_balance(&self, account: T::CrossAccountId) -> u32 {433		if <Balance<T>>::get((self.id, account)) != 0 {434			1435		} else {436			0437		}438	}439440	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {441		if token != TokenId::default() {442			return 0;443		}444		<Balance<T>>::get((self.id, account))445	}446447	fn allowance(448		&self,449		sender: T::CrossAccountId,450		spender: T::CrossAccountId,451		token: TokenId,452	) -> u128 {453		if token != TokenId::default() {454			return 0;455		}456		<Allowance<T>>::get((self.id, sender, spender))457	}458459	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {460		None461	}462463	fn total_pieces(&self, token: TokenId) -> Option<u128> {464		if token != TokenId::default() {465			return None;466		}467		<TotalSupply<T>>::try_get(self.id).ok()468	}469470	fn set_allowance_for_all(471		&self,472		_owner: T::CrossAccountId,473		_operator: T::CrossAccountId,474		_approve: bool,475	) -> DispatchResultWithPostInfo {476		fail!(<Error<T>>::SettingAllowanceForAllNotAllowed)477	}478479	fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {480		false481	}482483	/// Repairs a possibly broken item.484	fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {485		fail!(<Error<T>>::FungibleTokensAreAlwaysValid)486	}487}
after · pallets/fungible/src/common.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/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};20use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};21use pallet_common::{22	CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,23	weights::WeightInfo as _,24};25use pallet_structure::Error as StructureError;26use sp_runtime::ArithmeticError;27use sp_std::{vec::Vec, vec};28use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};2930use crate::{31	Allowance, TotalSupply, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf,32	weights::WeightInfo,33};3435pub struct CommonWeights<T: Config>(PhantomData<T>);36impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {37	fn create_item() -> Weight {38		<SelfWeightOf<T>>::create_item()39	}4041	fn create_multiple_items(_data: &[CreateItemData]) -> Weight {42		// All items minted for the same user, so it works same as create_item43		Self::create_item()44	}4546	fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {47		match data {48			CreateItemExData::Fungible(f) => {49				<SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)50			}51			_ => Weight::zero(),52		}53	}5455	fn burn_item() -> Weight {56		<SelfWeightOf<T>>::burn_item()57	}5859	fn set_collection_properties(amount: u32) -> Weight {60		<pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)61	}6263	fn delete_collection_properties(amount: u32) -> Weight {64		<pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)65	}6667	fn set_token_properties(_amount: u32) -> Weight {68		// Error69		Weight::zero()70	}7172	fn delete_token_properties(_amount: u32) -> Weight {73		// Error74		Weight::zero()75	}7677	fn set_token_property_permissions(_amount: u32) -> Weight {78		// Error79		Weight::zero()80	}8182	fn transfer() -> Weight {83		<SelfWeightOf<T>>::transfer()84	}8586	fn approve() -> Weight {87		<SelfWeightOf<T>>::approve()88	}8990	fn approve_from() -> Weight {91		<SelfWeightOf<T>>::approve_from()92	}9394	fn transfer_from() -> Weight {95		<SelfWeightOf<T>>::transfer_from()96	}9798	fn burn_from() -> Weight {99		<SelfWeightOf<T>>::burn_from()100	}101102	fn burn_recursively_self_raw() -> Weight {103		// Read to get total balance104		Self::burn_item() + T::DbWeight::get().reads(1)105	}106107	fn burn_recursively_breadth_raw(_amount: u32) -> Weight {108		// Fungible tokens can't have children109		Weight::zero()110	}111112	fn token_owner() -> Weight {113		Weight::zero()114	}115116	fn set_allowance_for_all() -> Weight {117		Weight::zero()118	}119120	fn force_repair_item() -> Weight {121		Weight::zero()122	}123}124125/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete126/// methods and adds weight info.127impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {128	fn mode(&self) -> up_data_structs::CollectionMode {129		self.0.mode()130	}131132	fn create_item(133		&self,134		sender: T::CrossAccountId,135		to: T::CrossAccountId,136		data: up_data_structs::CreateItemData,137		nesting_budget: &dyn Budget,138	) -> DispatchResultWithPostInfo {139		match data {140			up_data_structs::CreateItemData::Fungible(data) => with_weight(141				<Pallet<T>>::create_item(self, &sender, (to, data.value), nesting_budget),142				<CommonWeights<T>>::create_item(),143			),144			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),145		}146	}147148	fn create_multiple_items(149		&self,150		sender: T::CrossAccountId,151		to: T::CrossAccountId,152		data: Vec<up_data_structs::CreateItemData>,153		nesting_budget: &dyn Budget,154	) -> DispatchResultWithPostInfo {155		let mut sum: u128 = 0;156		for data in data {157			match data {158				up_data_structs::CreateItemData::Fungible(data) => {159					sum = sum160						.checked_add(data.value)161						.ok_or(ArithmeticError::Overflow)?;162				}163				_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),164			}165		}166167		with_weight(168			<Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),169			<CommonWeights<T>>::create_item(),170		)171	}172173	fn create_multiple_items_ex(174		&self,175		sender: <T>::CrossAccountId,176		data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,177		nesting_budget: &dyn Budget,178	) -> DispatchResultWithPostInfo {179		let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);180		let data = match data {181			up_data_structs::CreateItemExData::Fungible(f) => f,182			_ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),183		};184185		with_weight(186			<Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),187			weight,188		)189	}190191	fn burn_item(192		&self,193		sender: T::CrossAccountId,194		token: TokenId,195		amount: u128,196	) -> DispatchResultWithPostInfo {197		ensure!(198			token == TokenId::default(),199			<Error<T>>::FungibleItemsHaveNoId200		);201202		with_weight(203			<Pallet<T>>::burn(self, &sender, amount),204			<CommonWeights<T>>::burn_item(),205		)206	}207208	fn burn_item_recursively(209		&self,210		sender: T::CrossAccountId,211		token: TokenId,212		self_budget: &dyn Budget,213		_breadth_budget: &dyn Budget,214	) -> DispatchResultWithPostInfo {215		// Should not happen?216		ensure!(217			token == TokenId::default(),218			<Error<T>>::FungibleItemsHaveNoId219		);220		ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);221222		with_weight(223			<Pallet<T>>::burn(self, &sender, <Balance<T>>::get((self.id, &sender))),224			<CommonWeights<T>>::burn_recursively_self_raw(),225		)226	}227228	fn transfer(229		&self,230		from: T::CrossAccountId,231		to: T::CrossAccountId,232		token: TokenId,233		amount: u128,234		nesting_budget: &dyn Budget,235	) -> DispatchResultWithPostInfo {236		ensure!(237			token == TokenId::default(),238			<Error<T>>::FungibleItemsHaveNoId239		);240241		with_weight(242			<Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget),243			<CommonWeights<T>>::transfer(),244		)245	}246247	fn approve(248		&self,249		sender: T::CrossAccountId,250		spender: T::CrossAccountId,251		token: TokenId,252		amount: u128,253	) -> DispatchResultWithPostInfo {254		ensure!(255			token == TokenId::default(),256			<Error<T>>::FungibleItemsHaveNoId257		);258259		with_weight(260			<Pallet<T>>::set_allowance(self, &sender, &spender, amount),261			<CommonWeights<T>>::approve(),262		)263	}264265	fn approve_from(266		&self,267		sender: T::CrossAccountId,268		from: T::CrossAccountId,269		to: T::CrossAccountId,270		token: TokenId,271		amount: u128,272	) -> DispatchResultWithPostInfo {273		ensure!(274			token == TokenId::default(),275			<Error<T>>::FungibleItemsHaveNoId276		);277278		with_weight(279			<Pallet<T>>::set_allowance_from(self, &sender, &from, &to, amount),280			<CommonWeights<T>>::approve_from(),281		)282	}283284	fn transfer_from(285		&self,286		sender: T::CrossAccountId,287		from: T::CrossAccountId,288		to: T::CrossAccountId,289		token: TokenId,290		amount: u128,291		nesting_budget: &dyn Budget,292	) -> DispatchResultWithPostInfo {293		ensure!(294			token == TokenId::default(),295			<Error<T>>::FungibleItemsHaveNoId296		);297298		with_weight(299			<Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget),300			<CommonWeights<T>>::transfer_from(),301		)302	}303304	fn burn_from(305		&self,306		sender: T::CrossAccountId,307		from: T::CrossAccountId,308		token: TokenId,309		amount: u128,310		nesting_budget: &dyn Budget,311	) -> DispatchResultWithPostInfo {312		ensure!(313			token == TokenId::default(),314			<Error<T>>::FungibleItemsHaveNoId315		);316317		with_weight(318			<Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),319			<CommonWeights<T>>::burn_from(),320		)321	}322323	fn set_collection_properties(324		&self,325		sender: T::CrossAccountId,326		properties: Vec<Property>,327	) -> DispatchResultWithPostInfo {328		let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);329330		with_weight(331			<Pallet<T>>::set_collection_properties(self, &sender, properties),332			weight,333		)334	}335336	fn delete_collection_properties(337		&self,338		sender: &T::CrossAccountId,339		property_keys: Vec<PropertyKey>,340	) -> DispatchResultWithPostInfo {341		let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);342343		with_weight(344			<Pallet<T>>::delete_collection_properties(self, sender, property_keys),345			weight,346		)347	}348349	fn set_token_properties(350		&self,351		_sender: T::CrossAccountId,352		_token_id: TokenId,353		_property: Vec<Property>,354		_nesting_budget: &dyn Budget,355	) -> DispatchResultWithPostInfo {356		fail!(<Error<T>>::SettingPropertiesNotAllowed)357	}358359	fn set_token_property_permissions(360		&self,361		_sender: &T::CrossAccountId,362		_property_permissions: Vec<PropertyKeyPermission>,363	) -> DispatchResultWithPostInfo {364		fail!(<Error<T>>::SettingPropertiesNotAllowed)365	}366367	fn delete_token_properties(368		&self,369		_sender: T::CrossAccountId,370		_token_id: TokenId,371		_property_keys: Vec<PropertyKey>,372		_nesting_budget: &dyn Budget,373	) -> DispatchResultWithPostInfo {374		fail!(<Error<T>>::SettingPropertiesNotAllowed)375	}376377	fn check_nesting(378		&self,379		_sender: <T>::CrossAccountId,380		_from: (CollectionId, TokenId),381		_under: TokenId,382		_nesting_budget: &dyn Budget,383	) -> sp_runtime::DispatchResult {384		fail!(<Error<T>>::FungibleDisallowsNesting)385	}386387	fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}388389	fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}390391	fn collection_tokens(&self) -> Vec<TokenId> {392		vec![TokenId::default()]393	}394395	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {396		if <Balance<T>>::get((self.id, account)) != 0 {397			vec![TokenId::default()]398		} else {399			vec![]400		}401	}402403	fn token_exists(&self, token: TokenId) -> bool {404		token == TokenId::default()405	}406407	fn last_token_id(&self) -> TokenId {408		TokenId::default()409	}410411	fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {412		None413	}414415	/// Returns 10 tokens owners in no particular order.416	fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {417		<Pallet<T>>::token_owners(self.id, token).unwrap_or_default()418	}419420	fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {421		None422	}423424	fn token_properties(425		&self,426		_token_id: TokenId,427		_keys: Option<Vec<PropertyKey>>,428	) -> Vec<Property> {429		Vec::new()430	}431432	fn total_supply(&self) -> u32 {433		1434	}435436	fn account_balance(&self, account: T::CrossAccountId) -> u32 {437		if <Balance<T>>::get((self.id, account)) != 0 {438			1439		} else {440			0441		}442	}443444	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {445		if token != TokenId::default() {446			return 0;447		}448		<Balance<T>>::get((self.id, account))449	}450451	fn allowance(452		&self,453		sender: T::CrossAccountId,454		spender: T::CrossAccountId,455		token: TokenId,456	) -> u128 {457		if token != TokenId::default() {458			return 0;459		}460		<Allowance<T>>::get((self.id, sender, spender))461	}462463	fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {464		None465	}466467	fn total_pieces(&self, token: TokenId) -> Option<u128> {468		if token != TokenId::default() {469			return None;470		}471		<TotalSupply<T>>::try_get(self.id).ok()472	}473474	fn set_allowance_for_all(475		&self,476		_owner: T::CrossAccountId,477		_operator: T::CrossAccountId,478		_approve: bool,479	) -> DispatchResultWithPostInfo {480		fail!(<Error<T>>::SettingAllowanceForAllNotAllowed)481	}482483	fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {484		false485	}486487	/// Repairs a possibly broken item.488	fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {489		fail!(<Error<T>>::FungibleTokensAreAlwaysValid)490	}491}
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -152,6 +152,10 @@
 /// Implementation of `CommonCollectionOperations` for `NonfungibleHandle`. It wraps Nonfungible Pallete
 /// methods and adds weight info.
 impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {
+	fn mode(&self) -> up_data_structs::CollectionMode {
+		self.0.mode()
+	}
+
 	fn create_item(
 		&self,
 		sender: T::CrossAccountId,
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -741,7 +741,8 @@
 						Some((collection_id, nft_id)),
 						&target_nft_budget,
 					)
-					.map_err(Self::map_unique_err_to_proxy)?;
+					.map_err(Self::map_unique_err_to_proxy)?
+					.ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
 
 					approval_required = cross_sender != target_nft_owner;
 
@@ -989,7 +990,8 @@
 
 			let nft_owner =
 				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
-					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;
+					.map_err(|_| <Error<T>>::ResourceDoesntExist)?
+					.ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
 
 			Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {
 				ensure!(res.pending, <Error<T>>::ResourceNotPending);
@@ -1044,7 +1046,8 @@
 
 			let nft_owner =
 				<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
-					.map_err(|_| <Error<T>>::ResourceDoesntExist)?;
+					.map_err(|_| <Error<T>>::ResourceDoesntExist)?
+					.ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
 
 			ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);
 
@@ -1666,7 +1669,8 @@
 		let budget = budget::Value::new(NESTING_BUDGET);
 
 		let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
-			.map_err(Self::map_unique_err_to_proxy)?;
+			.map_err(Self::map_unique_err_to_proxy)?
+			.ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
 
 		let pending = sender != nft_owner;
 
@@ -1720,7 +1724,8 @@
 
 		let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
 		let topmost_owner =
-			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;
+			<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?
+				.ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
 
 		let sender = T::CrossAccountId::from_sub(sender);
 		if topmost_owner == sender {
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -186,6 +186,10 @@
 /// Implementation of `CommonCollectionOperations` for `RefungibleHandle`. It wraps Refungible Pallete
 /// methods and adds weight info.
 impl<T: Config> CommonCollectionOperations<T> for RefungibleHandle<T> {
+	fn mode(&self) -> up_data_structs::CollectionMode {
+		self.0.mode()
+	}
+
 	fn create_item(
 		&self,
 		sender: T::CrossAccountId,
modifiedpallets/structure/src/lib.rsdiffbeforeafterboth
--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -61,6 +61,7 @@
 use frame_support::fail;
 pub use pallet::*;
 use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};
+use up_data_structs::CollectionMode;
 use up_data_structs::{CollectionId, TokenId, mapping::TokenAddressMapping, budget::Budget};
 
 #[cfg(feature = "runtime-benchmarks")]
@@ -135,6 +136,8 @@
 	User(CrossAccountId),
 	/// Could not find the token provided as the owner.
 	TokenNotFound,
+	/// Nested token has multiple owners.
+	MultipleOwners,
 	/// Token owner is another token (still, the target token may not exist).
 	Token(CollectionId, TokenId),
 }
@@ -163,6 +166,10 @@
 				Some((collection, token)) => Parent::Token(collection, token),
 				None => Parent::User(owner),
 			},
+			None if handle.mode() == CollectionMode::ReFungible => handle
+				.total_pieces(token)
+				.map(|_| Parent::MultipleOwners)
+				.unwrap_or(Parent::TokenNotFound),
 			None => Parent::TokenNotFound,
 		})
 	}
@@ -203,19 +210,27 @@
 	///
 	/// May return token address if parent token not yet exists
 	///
+	/// Returns `None` if the token has multiple owners.
+	///
 	/// - `budget`: Limit for searching parents in depth.
 	pub fn find_topmost_owner(
 		collection: CollectionId,
 		token: TokenId,
 		budget: &dyn Budget,
-	) -> Result<T::CrossAccountId, DispatchError> {
+	) -> Result<Option<T::CrossAccountId>, DispatchError> {
 		let owner = Self::parent_chain(collection, token)
 			.take_while(|_| budget.consume())
-			.find(|p| matches!(p, Ok(Parent::User(_) | Parent::TokenNotFound)))
+			.find(|p| {
+				matches!(
+					p,
+					Ok(Parent::User(_) | Parent::TokenNotFound | Parent::MultipleOwners)
+				)
+			})
 			.ok_or(<Error<T>>::DepthLimit)??;
 
 		Ok(match owner {
-			Parent::User(v) => v,
+			Parent::User(v) => Some(v),
+			Parent::MultipleOwners => None,
 			_ => fail!(<Error<T>>::TokenNotFound),
 		})
 	}
@@ -223,13 +238,15 @@
 	/// Find the topmost parent and check that assigning `for_nest` token as a child for
 	/// `token` wouldn't create a cycle.
 	///
+	/// Returns `None` if the token has multiple owners.
+	///
 	/// - `budget`: Limit for searching parents in depth.
 	pub fn get_checked_topmost_owner(
 		collection: CollectionId,
 		token: TokenId,
 		for_nest: Option<(CollectionId, TokenId)>,
 		budget: &dyn Budget,
-	) -> Result<T::CrossAccountId, DispatchError> {
+	) -> Result<Option<T::CrossAccountId>, DispatchError> {
 		// Tried to nest token in itself
 		if Some((collection, token)) == for_nest {
 			return Err(<Error<T>>::OuroborosDetected.into());
@@ -242,8 +259,9 @@
 					return Err(<Error<T>>::OuroborosDetected.into())
 				}
 				// Token is owned by other user
-				Parent::User(user) => return Ok(user),
+				Parent::User(user) => return Ok(Some(user)),
 				Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
+				Parent::MultipleOwners => return Ok(None),
 				// Continue parent chain
 				Parent::Token(_, _) => {}
 			}
@@ -284,12 +302,17 @@
 		budget: &dyn Budget,
 	) -> Result<bool, DispatchError> {
 		let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
-			Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
+			Some((collection, token)) => match Self::find_topmost_owner(collection, token, budget)?
+			{
+				Some(topmost_owner) => topmost_owner,
+				None => return Ok(false),
+			},
 			None => user,
 		};
 
-		Self::get_checked_topmost_owner(collection, token, for_nest, budget)
-			.map(|indirect_owner| indirect_owner == target_parent)
+		Self::get_checked_topmost_owner(collection, token, for_nest, budget).map(|indirect_owner| {
+			indirect_owner.map_or(false, |indirect_owner| indirect_owner == target_parent)
+		})
 	}
 
 	/// Checks that `under` is valid token and that `token_id` could be nested under it
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -252,7 +252,7 @@
 /// Collection can represent various types of tokens.
 /// Each collection can contain only one type of tokens at a time.
 /// This type helps to understand which tokens the collection contains.
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
+#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub enum CollectionMode {
 	/// Non fungible tokens.
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -83,7 +83,7 @@
                 fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {
                     let budget = up_data_structs::budget::Value::new(10);
 
-                    Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))
+                    Ok(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?)
                 }
                 fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {
                     Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))