git.delta.rocks / unique-network / refs/commits / 6035f8d62cec

difftreelog

style cargo fmt

Farhad Hakimov2022-07-11parent: #1a56db7.patch.diff
in: master

3 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -329,7 +329,7 @@
 		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
 
 		/// Item was transferred.
-		/// 
+		///
 		/// # Arguments
 		///
 		/// * collection_id: ID of the collection to which the item belongs.
@@ -350,9 +350,9 @@
 		),
 
 		/// Sponsoring allowance was approved.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id
 		///
 		/// * item_id
@@ -371,51 +371,51 @@
 		),
 
 		/// Collection property was added or edited.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: ID of the collection, whose property was just set.
-		/// 
+		///
 		/// * property_key: Key of the property that was just set.
 		CollectionPropertySet(CollectionId, PropertyKey),
 
 		/// Collection property was deleted.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: ID of the collection, whose property was just deleted.
-		/// 
+		///
 		/// * property_key: Key of the property that was just deleted.
 		CollectionPropertyDeleted(CollectionId, PropertyKey),
 
 		/// Item property was added or edited.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: ID of the collection, whose token's property was just set.
-		/// 
+		///
 		/// * item_id: ID of the item, whose property was just set.
-		/// 
+		///
 		/// * property_key: Key of the property that was just set.
 		TokenPropertySet(CollectionId, TokenId, PropertyKey),
 
 		/// Item property was deleted.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: ID of the collection, whose token's property was just deleted.
-		/// 
+		///
 		/// * item_id: ID of the item, whose property was just deleted.
-		/// 
+		///
 		/// * property_key: Key of the property that was just deleted.
 		TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),
 
 		/// Token property permission was added or updated for a collection.
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: ID of the collection, whose permissions were just set/updated.
-		/// 
+		///
 		/// * property_key: Key of the property of the set/updated permission.
 		PropertyPermissionSet(CollectionId, PropertyKey),
 	}
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
before · pallets/refungible/src/lib.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{ensure, BoundedVec};20use up_data_structs::{21	AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,22	CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping, budget::Budget,23};24use pallet_evm::account::CrossAccountId;25use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};26use pallet_structure::Pallet as PalletStructure;27use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};28use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};29use core::ops::Deref;30use codec::{Encode, Decode, MaxEncodedLen};31use scale_info::TypeInfo;3233pub use pallet::*;34#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod common;37pub mod erc;38pub mod weights;39pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4041/// Token data, stored independently from other data used to describe it.42/// Notably contains the token metadata.43#[struct_versioning::versioned(version = 2, upper)]44#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]45pub struct ItemData {46	pub const_data: BoundedVec<u8, CustomDataLimit>,4748	#[version(..2)]49	pub variable_data: BoundedVec<u8, CustomDataLimit>,50}5152#[frame_support::pallet]53pub mod pallet {54	use super::*;55	use frame_support::{56		Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,57		traits::StorageVersion,58	};59	use frame_system::pallet_prelude::*;60	use up_data_structs::{CollectionId, TokenId};61	use super::weights::WeightInfo;6263	#[pallet::error]64	pub enum Error<T> {65		/// Not Refungible item data used to mint in Refungible collection.66		NotRefungibleDataUsedToMintFungibleCollectionToken,67		/// Maximum refungibility exceeded68		WrongRefungiblePieces,69		/// Refungible token can't be repartitioned by user who isn't owns all pieces70		RepartitionWhileNotOwningAllPieces,71		/// Refungible token can't nest other tokens72		RefungibleDisallowsNesting,73		/// Setting item properties is not allowed74		SettingPropertiesNotAllowed,75	}7677	#[pallet::config]78	pub trait Config:79		frame_system::Config + pallet_common::Config + pallet_structure::Config80	{81		type WeightInfo: WeightInfo;82	}8384	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);8586	#[pallet::pallet]87	#[pallet::storage_version(STORAGE_VERSION)]88	#[pallet::generate_store(pub(super) trait Store)]89	pub struct Pallet<T>(_);9091	/// Total amount of minted tokens in a collection.92	#[pallet::storage]93	pub type TokensMinted<T: Config> =94		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;95	96	/// Amount of tokens burnt in a collection.97	#[pallet::storage]98	pub type TokensBurnt<T: Config> =99		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;100101	/// Token data, used to partially describe a token.102	#[pallet::storage]103	pub type TokenData<T: Config> = StorageNMap<104		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),105		Value = ItemData,106		QueryKind = ValueQuery,107	>;108109	/// Amount of pieces a refungible token is split into.110	#[pallet::storage]111	pub type TotalSupply<T: Config> = StorageNMap<112		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),113		Value = u128,114		QueryKind = ValueQuery,115	>;116117	/// Used to enumerate tokens owned by account.118	#[pallet::storage]119	pub type Owned<T: Config> = StorageNMap<120		Key = (121			Key<Twox64Concat, CollectionId>,122			Key<Blake2_128Concat, T::CrossAccountId>,123			Key<Twox64Concat, TokenId>,124		),125		Value = bool,126		QueryKind = ValueQuery,127	>;128129	/// Amount of tokens (not pieces) partially owned by an account within a collection.130	#[pallet::storage]131	pub type AccountBalance<T: Config> = StorageNMap<132		Key = (133			Key<Twox64Concat, CollectionId>,134			// Owner135			Key<Blake2_128Concat, T::CrossAccountId>,136		),137		Value = u32,138		QueryKind = ValueQuery,139	>;140141	/// Amount of pieces of a token owned by an account.142	#[pallet::storage]143	pub type Balance<T: Config> = StorageNMap<144		Key = (145			Key<Twox64Concat, CollectionId>,146			Key<Twox64Concat, TokenId>,147			// Owner148			Key<Blake2_128Concat, T::CrossAccountId>,149		),150		Value = u128,151		QueryKind = ValueQuery,152	>;153154	/// todo:doc155	#[pallet::storage]156	pub type Allowance<T: Config> = StorageNMap<157		Key = (158			Key<Twox64Concat, CollectionId>,159			Key<Twox64Concat, TokenId>,160			// Owner161			Key<Blake2_128, T::CrossAccountId>,162			// Spender163			Key<Blake2_128Concat, T::CrossAccountId>,164		),165		Value = u128,166		QueryKind = ValueQuery,167	>;168169	#[pallet::hooks]170	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {171		fn on_runtime_upgrade() -> Weight {172			if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {173				<TokenData<T>>::translate_values::<ItemDataVersion1, _>(|v| {174					Some(<ItemDataVersion2>::from(v))175				})176			}177178			0179		}180	}181}182183pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);184impl<T: Config> RefungibleHandle<T> {185	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {186		Self(inner)187	}188	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {189		self.0190	}191}192impl<T: Config> Deref for RefungibleHandle<T> {193	type Target = pallet_common::CollectionHandle<T>;194195	fn deref(&self) -> &Self::Target {196		&self.0197	}198}199200impl<T: Config> Pallet<T> {201	pub fn total_supply(collection: &RefungibleHandle<T>) -> u32 {202		<TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)203	}204	pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {205		<TotalSupply<T>>::contains_key((collection.id, token))206	}207}208209// unchecked calls skips any permission checks210impl<T: Config> Pallet<T> {211	pub fn init_collection(212		owner: T::CrossAccountId,213		data: CreateCollectionData<T::AccountId>,214	) -> Result<CollectionId, DispatchError> {215		<PalletCommon<T>>::init_collection(owner, data, false)216	}217	pub fn destroy_collection(218		collection: RefungibleHandle<T>,219		sender: &T::CrossAccountId,220	) -> DispatchResult {221		let id = collection.id;222223		if Self::collection_has_tokens(id) {224			return Err(<CommonError<T>>::CantDestroyNotEmptyCollection.into());225		}226227		// =========228229		PalletCommon::destroy_collection(collection.0, sender)?;230231		<TokensMinted<T>>::remove(id);232		<TokensBurnt<T>>::remove(id);233		<TokenData<T>>::remove_prefix((id,), None);234		<TotalSupply<T>>::remove_prefix((id,), None);235		<Balance<T>>::remove_prefix((id,), None);236		<Allowance<T>>::remove_prefix((id,), None);237		<Owned<T>>::remove_prefix((id,), None);238		<AccountBalance<T>>::remove_prefix((id,), None);239		Ok(())240	}241242	fn collection_has_tokens(collection_id: CollectionId) -> bool {243		<TokenData<T>>::iter_prefix((collection_id,))244			.next()245			.is_some()246	}247248	pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {249		let burnt = <TokensBurnt<T>>::get(collection.id)250			.checked_add(1)251			.ok_or(ArithmeticError::Overflow)?;252253		<TokensBurnt<T>>::insert(collection.id, burnt);254		<TokenData<T>>::remove((collection.id, token_id));255		<TotalSupply<T>>::remove((collection.id, token_id));256		<Balance<T>>::remove_prefix((collection.id, token_id), None);257		<Allowance<T>>::remove_prefix((collection.id, token_id), None);258		// TODO: ERC721 transfer event259		Ok(())260	}261	262	pub fn burn(263		collection: &RefungibleHandle<T>,264		owner: &T::CrossAccountId,265		token: TokenId,266		amount: u128,267	) -> DispatchResult {268		let total_supply = <TotalSupply<T>>::get((collection.id, token))269			.checked_sub(amount)270			.ok_or(<CommonError<T>>::TokenValueTooLow)?;271272		// This was probally last owner of this token?273		if total_supply == 0 {274			// Ensure user actually owns this amount275			ensure!(276				<Balance<T>>::get((collection.id, token, owner)) == amount,277				<CommonError<T>>::TokenValueTooLow278			);279			let account_balance = <AccountBalance<T>>::get((collection.id, owner))280				.checked_sub(1)281				// Should not occur282				.ok_or(ArithmeticError::Underflow)?;283284			// =========285286			<Owned<T>>::remove((collection.id, owner, token));287			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);288			<AccountBalance<T>>::insert((collection.id, owner), account_balance);289			Self::burn_token(collection, token)?;290			<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(291				collection.id,292				token,293				owner.clone(),294				amount,295			));296			return Ok(());297		}298299		let balance = <Balance<T>>::get((collection.id, token, owner))300			.checked_sub(amount)301			.ok_or(<CommonError<T>>::TokenValueTooLow)?;302		let account_balance = if balance == 0 {303			<AccountBalance<T>>::get((collection.id, owner))304				.checked_sub(1)305				// Should not occur306				.ok_or(ArithmeticError::Underflow)?307		} else {308			0309		};310311		// =========312313		if balance == 0 {314			<Owned<T>>::remove((collection.id, owner, token));315			<PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);316			<Balance<T>>::remove((collection.id, token, owner));317			<AccountBalance<T>>::insert((collection.id, owner), account_balance);318		} else {319			<Balance<T>>::insert((collection.id, token, owner), balance);320		}321		<TotalSupply<T>>::insert((collection.id, token), total_supply);322		// TODO: ERC20 transfer event323		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(324			collection.id,325			token,326			owner.clone(),327			amount,328		));329		Ok(())330	}331332	pub fn transfer(333		collection: &RefungibleHandle<T>,334		from: &T::CrossAccountId,335		to: &T::CrossAccountId,336		token: TokenId,337		amount: u128,338		nesting_budget: &dyn Budget,339	) -> DispatchResult {340		ensure!(341			collection.limits.transfers_enabled(),342			<CommonError<T>>::TransferNotAllowed343		);344345		if collection.permissions.access() == AccessMode::AllowList {346			collection.check_allowlist(from)?;347			collection.check_allowlist(to)?;348		}349		<PalletCommon<T>>::ensure_correct_receiver(to)?;350351		let balance_from = <Balance<T>>::get((collection.id, token, from))352			.checked_sub(amount)353			.ok_or(<CommonError<T>>::TokenValueTooLow)?;354		let mut create_target = false;355		let from_to_differ = from != to;356		let balance_to = if from != to {357			let old_balance = <Balance<T>>::get((collection.id, token, to));358			if old_balance == 0 {359				create_target = true;360			}361			Some(362				old_balance363					.checked_add(amount)364					.ok_or(ArithmeticError::Overflow)?,365			)366		} else {367			None368		};369370		let account_balance_from = if balance_from == 0 {371			Some(372				<AccountBalance<T>>::get((collection.id, from))373					.checked_sub(1)374					// Should not occur375					.ok_or(ArithmeticError::Underflow)?,376			)377		} else {378			None379		};380		// Account data is created in token, AccountBalance should be increased381		// But only if from != to as we shouldn't check overflow in this case382		let account_balance_to = if create_target && from_to_differ {383			let account_balance_to = <AccountBalance<T>>::get((collection.id, to))384				.checked_add(1)385				.ok_or(ArithmeticError::Overflow)?;386			ensure!(387				account_balance_to < collection.limits.account_token_ownership_limit(),388				<CommonError<T>>::AccountTokenLimitExceeded,389			);390391			Some(account_balance_to)392		} else {393			None394		};395396		// =========397398		<PalletStructure<T>>::nest_if_sent_to_token(399			from.clone(),400			to,401			collection.id,402			token,403			nesting_budget,404		)?;405406		if let Some(balance_to) = balance_to {407			// from != to408			if balance_from == 0 {409				<Balance<T>>::remove((collection.id, token, from));410				<PalletStructure<T>>::unnest_if_nested(from, collection.id, token);411			} else {412				<Balance<T>>::insert((collection.id, token, from), balance_from);413			}414			<Balance<T>>::insert((collection.id, token, to), balance_to);415			if let Some(account_balance_from) = account_balance_from {416				<AccountBalance<T>>::insert((collection.id, from), account_balance_from);417				<Owned<T>>::remove((collection.id, from, token));418			}419			if let Some(account_balance_to) = account_balance_to {420				<AccountBalance<T>>::insert((collection.id, to), account_balance_to);421				<Owned<T>>::insert((collection.id, to, token), true);422			}423		}424425		// TODO: ERC20 transfer event426		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(427			collection.id,428			token,429			from.clone(),430			to.clone(),431			amount,432		));433		Ok(())434	}435436	pub fn create_multiple_items(437		collection: &RefungibleHandle<T>,438		sender: &T::CrossAccountId,439		data: Vec<CreateRefungibleExData<T::CrossAccountId>>,440		nesting_budget: &dyn Budget,441	) -> DispatchResult {442		if !collection.is_owner_or_admin(sender) {443			ensure!(444				collection.permissions.mint_mode(),445				<CommonError<T>>::PublicMintingNotAllowed446			);447			collection.check_allowlist(sender)?;448449			for item in data.iter() {450				for user in item.users.keys() {451					collection.check_allowlist(user)?;452				}453			}454		}455456		for item in data.iter() {457			for (owner, _) in item.users.iter() {458				<PalletCommon<T>>::ensure_correct_receiver(owner)?;459			}460		}461462		// Total pieces per tokens463		let totals = data464			.iter()465			.map(|data| {466				Ok(data467					.users468					.iter()469					.map(|u| u.1)470					.try_fold(0u128, |acc, v| acc.checked_add(*v))471					.ok_or(ArithmeticError::Overflow)?)472			})473			.collect::<Result<Vec<_>, DispatchError>>()?;474		for total in &totals {475			ensure!(476				*total <= MAX_REFUNGIBLE_PIECES,477				<Error<T>>::WrongRefungiblePieces478			);479		}480481		let first_token_id = <TokensMinted<T>>::get(collection.id);482		let tokens_minted = first_token_id483			.checked_add(data.len() as u32)484			.ok_or(ArithmeticError::Overflow)?;485		ensure!(486			tokens_minted < collection.limits.token_limit(),487			<CommonError<T>>::CollectionTokenLimitExceeded488		);489490		let mut balances = BTreeMap::new();491		for data in &data {492			for owner in data.users.keys() {493				let balance = balances494					.entry(owner)495					.or_insert_with(|| <AccountBalance<T>>::get((collection.id, owner)));496				*balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;497498				ensure!(499					*balance <= collection.limits.account_token_ownership_limit(),500					<CommonError<T>>::AccountTokenLimitExceeded,501				);502			}503		}504505		for (i, token) in data.iter().enumerate() {506			let token_id = TokenId(first_token_id + i as u32 + 1);507			for (to, _) in token.users.iter() {508				<PalletStructure<T>>::check_nesting(509					sender.clone(),510					to,511					collection.id,512					token_id,513					nesting_budget,514				)?;515			}516		}517518		// =========519520		<TokensMinted<T>>::insert(collection.id, tokens_minted);521		for (account, balance) in balances {522			<AccountBalance<T>>::insert((collection.id, account), balance);523		}524		for (i, token) in data.into_iter().enumerate() {525			let token_id = first_token_id + i as u32 + 1;526			<TotalSupply<T>>::insert((collection.id, token_id), totals[i]);527528			<TokenData<T>>::insert(529				(collection.id, token_id),530				ItemData {531					const_data: token.const_data,532				},533			);534535			for (user, amount) in token.users.into_iter() {536				if amount == 0 {537					continue;538				}539				<Balance<T>>::insert((collection.id, token_id, &user), amount);540				<Owned<T>>::insert((collection.id, &user, TokenId(token_id)), true);541				<PalletStructure<T>>::nest_if_sent_to_token_unchecked(542					&user,543					collection.id,544					TokenId(token_id),545				);546547				// TODO: ERC20 transfer event548				<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(549					collection.id,550					TokenId(token_id),551					user,552					amount,553				));554			}555		}556		Ok(())557	}558559	pub fn set_allowance_unchecked(560		collection: &RefungibleHandle<T>,561		sender: &T::CrossAccountId,562		spender: &T::CrossAccountId,563		token: TokenId,564		amount: u128,565	) {566		if amount == 0 {567			<Allowance<T>>::remove((collection.id, token, sender, spender));568		} else {569			<Allowance<T>>::insert((collection.id, token, sender, spender), amount);570		}571		// TODO: ERC20 approval event572		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(573			collection.id,574			token,575			sender.clone(),576			spender.clone(),577			amount,578		))579	}580581	pub fn set_allowance(582		collection: &RefungibleHandle<T>,583		sender: &T::CrossAccountId,584		spender: &T::CrossAccountId,585		token: TokenId,586		amount: u128,587	) -> DispatchResult {588		if collection.permissions.access() == AccessMode::AllowList {589			collection.check_allowlist(sender)?;590			collection.check_allowlist(spender)?;591		}592593		<PalletCommon<T>>::ensure_correct_receiver(spender)?;594595		if <Balance<T>>::get((collection.id, token, sender)) < amount {596			ensure!(597				collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),598				<CommonError<T>>::CantApproveMoreThanOwned599			);600		}601602		// =========603604		Self::set_allowance_unchecked(collection, sender, spender, token, amount);605		Ok(())606	}607608	/// todo:doc oh look, a precedent. not pub, too. but it has an unclear use-case.609	/// Returns allowance, which should be set after transaction610	fn check_allowed(611		collection: &RefungibleHandle<T>,612		spender: &T::CrossAccountId,613		from: &T::CrossAccountId,614		token: TokenId,615		amount: u128,616		nesting_budget: &dyn Budget,617	) -> Result<Option<u128>, DispatchError> {618		if spender.conv_eq(from) {619			return Ok(None);620		}621		if collection.permissions.access() == AccessMode::AllowList {622			// `from`, `to` checked in [`transfer`]623			collection.check_allowlist(spender)?;624		}625		if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {626			// TODO: should collection owner be allowed to perform this transfer?627			ensure!(628				<PalletStructure<T>>::check_indirectly_owned(629					spender.clone(),630					source.0,631					source.1,632					None,633					nesting_budget634				)?,635				<CommonError<T>>::ApprovedValueTooLow,636			);637			return Ok(None);638		}639		let allowance =640			<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);641		if allowance.is_none() {642			ensure!(643				collection.ignores_allowance(spender),644				<CommonError<T>>::ApprovedValueTooLow645			);646		}647		Ok(allowance)648	}649650	pub fn transfer_from(651		collection: &RefungibleHandle<T>,652		spender: &T::CrossAccountId,653		from: &T::CrossAccountId,654		to: &T::CrossAccountId,655		token: TokenId,656		amount: u128,657		nesting_budget: &dyn Budget,658	) -> DispatchResult {659		let allowance =660			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;661662		// =========663664		Self::transfer(collection, from, to, token, amount, nesting_budget)?;665		if let Some(allowance) = allowance {666			Self::set_allowance_unchecked(collection, from, spender, token, allowance);667		}668		Ok(())669	}670671	pub fn burn_from(672		collection: &RefungibleHandle<T>,673		spender: &T::CrossAccountId,674		from: &T::CrossAccountId,675		token: TokenId,676		amount: u128,677		nesting_budget: &dyn Budget,678	) -> DispatchResult {679		let allowance =680			Self::check_allowed(collection, spender, from, token, amount, nesting_budget)?;681682		// =========683684		Self::burn(collection, from, token, amount)?;685		if let Some(allowance) = allowance {686			Self::set_allowance_unchecked(collection, from, spender, token, allowance);687		}688		Ok(())689	}690691	/// Delegated to `create_multiple_items`692	pub fn create_item(693		collection: &RefungibleHandle<T>,694		sender: &T::CrossAccountId,695		data: CreateRefungibleExData<T::CrossAccountId>,696		nesting_budget: &dyn Budget,697	) -> DispatchResult {698		Self::create_multiple_items(collection, sender, vec![data], nesting_budget)699	}700701	pub fn repartition(702		collection: &RefungibleHandle<T>,703		owner: &T::CrossAccountId,704		token: TokenId,705		amount: u128,706	) -> DispatchResult {707		ensure!(708			amount <= MAX_REFUNGIBLE_PIECES,709			<Error<T>>::WrongRefungiblePieces710		);711		ensure!(amount > 0, <CommonError<T>>::TokenValueTooLow);712		// Ensure user owns all pieces713		let total_supply = <TotalSupply<T>>::get((collection.id, token));714		let balance = <Balance<T>>::get((collection.id, token, owner));715		ensure!(716			total_supply == balance,717			<Error<T>>::RepartitionWhileNotOwningAllPieces718		);719720		<Balance<T>>::insert((collection.id, token, owner), amount);721		<TotalSupply<T>>::insert((collection.id, token), amount);722		Ok(())723	}724725	fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {726		<TotalSupply<T>>::try_get((collection_id, token_id)).ok()727	}728}
modifiedpallets/unique/src/lib.rsdiffbeforeafterboth
--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -163,9 +163,9 @@
 		CollectionLimitSet(CollectionId),
 
 		/// Collection permissions were set
-		/// 
+		///
 		/// # Arguments
-		/// 
+		///
 		/// * collection_id: Globally unique collection identifier.
 		CollectionPermissionSet(CollectionId),
 	}
@@ -219,7 +219,7 @@
 		/// Collection id (controlled?2), token id (controlled?2)
 		#[deprecated]
 		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
-		/// Last sponsoring of token property setting // todo:doc rephrase this and the following 
+		/// Last sponsoring of token property setting // todo:doc rephrase this and the following
 		pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;
 
 		/// Last sponsoring of NFT approval in a collection
@@ -294,7 +294,7 @@
 		/// * Anyone.
 		///
 		/// # Arguments
-		/// 
+		///
 		/// * data: explicit create-collection data.
 		#[weight = <SelfWeightOf<T>>::create_collection()]
 		#[transactional]
@@ -495,7 +495,7 @@
 		}
 
 		/// Set (invite) a new collection sponsor. If successful, confirmation from the sponsor-to-be will be pending.
-		/// 
+		///
 		/// # Permissions
 		///
 		/// * Collection Owner
@@ -526,7 +526,7 @@
 		}
 
 		/// Confirm own sponsorship of a collection.
-		/// 
+		///
 		/// # Permissions
 		///
 		/// * The sponsor to-be
@@ -699,7 +699,7 @@
 		/// # Arguments
 		///
 		/// * collection_id.
-		/// 
+		///
 		/// * token_id.
 		///
 		/// * properties: a vector of key-value pairs stored as the token's metadata. Keys support Latin letters, '-', '_', and '.' as symbols.
@@ -778,7 +778,7 @@
 		}
 
 		/// Create multiple items inside a collection with explicitly specified initial parameters.
-		/// 
+		///
 		/// # Permissions
 		///
 		/// * Collection Owner
@@ -792,7 +792,7 @@
 		///
 		/// * collection_id: ID of the collection.
 		///
-		/// * data: explicit item creation data. 
+		/// * data: explicit item creation data.
 		#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]
 		#[transactional]
 		pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
@@ -939,7 +939,7 @@
 		/// 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.
 		///
 		/// # Permissions
-		/// 
+		///
 		/// * Collection Owner
 		/// * Collection Admin
 		/// * Current NFT owner
@@ -968,7 +968,7 @@
 		/// Set specific limits of a collection. Empty, or None fields mean chain default.
 		///.
 		/// # Permissions
-		/// 
+		///
 		/// * Collection Owner
 		/// * Collection Admin
 		///
@@ -1002,7 +1002,7 @@
 		/// Set specific permissions of a collection. Empty, or None fields mean chain default.
 		///
 		/// # Permissions
-		/// 
+		///
 		/// * Collection Owner
 		/// * Collection Admin
 		///
@@ -1036,15 +1036,15 @@
 		/// Re-partition a refungible token, while owning all of its parts.
 		///
 		/// # Permissions
-		/// 
+		///
 		/// * Token Owner (must own every part)
 		///
 		/// # Arguments
 		///
 		/// * collection_id.
-		/// 
-		/// * token: the ID of the RFT. 
 		///
+		/// * token: the ID of the RFT.
+		///
 		/// * amount: The new number of parts into which the token shall be partitioned.
 		#[weight = T::RefungibleExtensionsWeightInfo::repartition()]
 		#[transactional]