git.delta.rocks / unique-network / refs/commits / 44992fca8f8c

difftreelog

feat burn_from

Yaroslav Bolyukin2021-11-02parent: #283713e.patch.diff
in: master

12 files changed

modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -459,6 +459,7 @@
 	fn transfer() -> Weight;
 	fn approve() -> Weight;
 	fn transfer_from() -> Weight;
+	fn burn_from() -> Weight;
 	fn set_variable_metadata(bytes: u32) -> Weight;
 }
 
@@ -504,6 +505,13 @@
 		token: TokenId,
 		amount: u128,
 	) -> DispatchResultWithPostInfo;
+	fn burn_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo;
 
 	fn set_variable_metadata(
 		&self,
modifiedpallets/fungible/src/common.rsdiffbeforeafterboth
--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -38,6 +38,10 @@
 		<SelfWeightOf<T>>::transfer_from()
 	}
 
+	fn burn_from() -> Weight {
+		0
+	}
+
 	fn set_variable_metadata(_bytes: u32) -> Weight {
 		// Error
 		0
@@ -156,6 +160,24 @@
 		)
 	}
 
+	fn burn_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(
+			token == TokenId::default(),
+			<Error<T>>::FungibleItemsHaveNoId
+		);
+
+		with_weight(
+			<Pallet<T>>::burn_from(&self, &sender, &from, amount),
+			<CommonWeights<T>>::burn_from(),
+		)
+	}
+
 	fn set_variable_metadata(
 		&self,
 		_sender: T::CrossAccountId,
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -96,6 +96,18 @@
 	}
 }
 
+#[solidity_interface(name = "ERC20UniqueExtensions")]
+impl<T: Config> FungibleHandle<T> {
+	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let from = T::CrossAccountId::from_eth(from);
+		let amount = amount.try_into().map_err(|_| "amount overflow")?;
+
+		<Pallet<T>>::burn_from(self, &caller, &from, amount).map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
+}
+
 #[solidity_interface(name = "UniqueFungible", is(ERC20))]
 impl<T: Config> FungibleHandle<T> {}
 
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -356,6 +356,38 @@
 		Ok(())
 	}
 
+	pub fn burn_from(
+		collection: &FungibleHandle<T>,
+		spender: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		amount: u128,
+	) -> DispatchResult {
+		if spender == from {
+			return Self::burn(collection, from, amount);
+		}
+		if collection.access == AccessMode::WhiteList {
+			// `from` checked in [`burn`]
+			collection.check_allowlist(spender)?;
+		}
+
+		let allowance = <Allowance<T>>::get((collection.id, from.as_sub(), spender.as_sub()))
+			.checked_sub(amount);
+		if allowance.is_none() {
+			ensure!(
+				collection.ignores_allowance(spender)?,
+				<CommonError<T>>::TokenValueNotEnough
+			);
+		}
+
+		// =========
+
+		Self::burn(collection, from, amount)?;
+		if let Some(allowance) = allowance {
+			Self::set_allowance_unchecked(collection, from, spender, allowance);
+		}
+		Ok(())
+	}
+
 	/// Delegated to `create_multiple_items`
 	pub fn create_item(
 		collection: &FungibleHandle<T>,
modifiedpallets/nft/src/common.rsdiffbeforeafterboth
--- a/pallets/nft/src/common.rs
+++ b/pallets/nft/src/common.rs
@@ -45,4 +45,8 @@
 	fn set_variable_metadata(bytes: u32) -> nft_data_structs::Weight {
 		dispatch_weight::<T>() + max_weight_of!(set_variable_metadata(bytes))
 	}
+
+	fn burn_from() -> Weight {
+		dispatch_weight::<T>() + max_weight_of!(burn_from())
+	}
 }
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -35,9 +35,10 @@
 				.map_err(|_| AnyError)?
 				.ok_or(AnyError)?;
 			match call {
-				UniqueNFTCall::ERC721UniqueExtensions(
-					ERC721UniqueExtensionsCall::TransferNft { token_id, .. },
-				)
+				UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {
+					token_id,
+					..
+				})
 				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {
 					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;
 					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -623,14 +623,13 @@
 		/// * item_id: ID of NFT to burn.
 		///
 		/// * from: owner of item
-		// #[weight = 0]
-		// #[transactional]
-		// pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> PostDispatchInfo {
-		// 	let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+		#[weight = <CommonWeights<T>>::burn_from()]
+		#[transactional]
+		pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
+			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
 
-		// 	// dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))
-		// 	todo!()
-		// }
+			dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))
+		}
 
 		/// Change ownership of the token.
 		///
modifiedpallets/nonfungible/src/common.rsdiffbeforeafterboth
before · pallets/nonfungible/src/common.rs
1use core::marker::PhantomData;23use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};4use nft_data_structs::TokenId;5use pallet_common::{6	CommonCollectionOperations, CommonWeightInfo, account::CrossAccountId, with_weight,7};8use sp_runtime::DispatchError;9use sp_std::vec::Vec;1011use crate::{12	AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,13	SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,14};1516pub struct CommonWeights<T: Config>(PhantomData<T>);17impl<T: Config> CommonWeightInfo for CommonWeights<T> {18	fn create_item() -> Weight {19		<SelfWeightOf<T>>::create_item()20	}2122	fn create_multiple_items(amount: u32) -> Weight {23		<SelfWeightOf<T>>::create_multiple_items(amount)24	}2526	fn burn_item() -> Weight {27		<SelfWeightOf<T>>::burn_item()28	}2930	fn transfer() -> Weight {31		<SelfWeightOf<T>>::transfer()32	}3334	fn approve() -> Weight {35		<SelfWeightOf<T>>::approve()36	}3738	fn transfer_from() -> Weight {39		<SelfWeightOf<T>>::transfer_from()40	}4142	fn set_variable_metadata(bytes: u32) -> Weight {43		<SelfWeightOf<T>>::set_variable_metadata(bytes)44	}45}4647fn map_create_data<T: Config>(48	data: nft_data_structs::CreateItemData,49	to: &T::CrossAccountId,50) -> Result<CreateItemData<T>, DispatchError> {51	match data {52		nft_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData {53			const_data: data.const_data,54			variable_data: data.variable_data,55			owner: to.clone(),56		}),57		_ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),58	}59}6061impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {62	fn create_item(63		&self,64		sender: T::CrossAccountId,65		to: T::CrossAccountId,66		data: nft_data_structs::CreateItemData,67	) -> DispatchResultWithPostInfo {68		with_weight(69			<Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),70			<CommonWeights<T>>::create_item(),71		)72	}7374	fn create_multiple_items(75		&self,76		sender: T::CrossAccountId,77		to: T::CrossAccountId,78		data: Vec<nft_data_structs::CreateItemData>,79	) -> DispatchResultWithPostInfo {80		let data = data81			.into_iter()82			.map(|d| map_create_data::<T>(d, &to))83			.collect::<Result<Vec<_>, DispatchError>>()?;8485		let amount = data.len();86		with_weight(87			<Pallet<T>>::create_multiple_items(self, &sender, data),88			<CommonWeights<T>>::create_multiple_items(amount as u32),89		)90	}9192	fn burn_item(93		&self,94		sender: T::CrossAccountId,95		token: TokenId,96		amount: u128,97	) -> DispatchResultWithPostInfo {98		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);99		if amount == 1 {100			with_weight(101				<Pallet<T>>::burn(&self, &sender, token),102				<CommonWeights<T>>::burn_item(),103			)104		} else {105			Ok(().into())106		}107	}108109	fn transfer(110		&self,111		from: T::CrossAccountId,112		to: T::CrossAccountId,113		token: TokenId,114		amount: u128,115	) -> DispatchResultWithPostInfo {116		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);117		if amount == 1 {118			with_weight(119				<Pallet<T>>::transfer(&self, &from, &to, token),120				<CommonWeights<T>>::transfer(),121			)122		} else {123			Ok(().into())124		}125	}126127	fn approve(128		&self,129		sender: T::CrossAccountId,130		spender: T::CrossAccountId,131		token: TokenId,132		amount: u128,133	) -> DispatchResultWithPostInfo {134		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);135136		with_weight(137			if amount == 1 {138				<Pallet<T>>::set_allowance(&self, &sender, token, Some(&spender))139			} else {140				<Pallet<T>>::set_allowance(&self, &sender, token, None)141			},142			<CommonWeights<T>>::approve(),143		)144	}145146	fn transfer_from(147		&self,148		sender: T::CrossAccountId,149		from: T::CrossAccountId,150		to: T::CrossAccountId,151		token: TokenId,152		amount: u128,153	) -> DispatchResultWithPostInfo {154		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);155156		if amount == 1 {157			with_weight(158				<Pallet<T>>::transfer_from(&self, &sender, &from, &to, token),159				<CommonWeights<T>>::transfer_from(),160			)161		} else {162			Ok(().into())163		}164	}165166	fn set_variable_metadata(167		&self,168		sender: T::CrossAccountId,169		token: TokenId,170		data: Vec<u8>,171	) -> DispatchResultWithPostInfo {172		let len = data.len();173		with_weight(174			<Pallet<T>>::set_variable_metadata(&self, &sender, token, data),175			<CommonWeights<T>>::set_variable_metadata(len as u32),176		)177	}178179	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {180		<Owned<T>>::iter_prefix((self.id, account.as_sub()))181			.map(|(id, _)| id)182			.collect()183	}184185	fn token_exists(&self, token: TokenId) -> bool {186		<Pallet<T>>::token_exists(self, token)187	}188189	fn last_token_id(&self) -> TokenId {190		TokenId(<TokensMinted<T>>::get(self.id))191	}192193	fn token_owner(&self, token: TokenId) -> T::CrossAccountId {194		<TokenData<T>>::get((self.id, token))195			.map(|t| t.owner)196			.unwrap_or_default()197	}198	fn const_metadata(&self, token: TokenId) -> Vec<u8> {199		<TokenData<T>>::get((self.id, token))200			.map(|t| t.const_data.clone())201			.unwrap_or_default()202	}203	fn variable_metadata(&self, token: TokenId) -> Vec<u8> {204		<TokenData<T>>::get((self.id, token))205			.map(|t| t.variable_data.clone())206			.unwrap_or_default()207	}208209	fn collection_tokens(&self) -> u32 {210		<Pallet<T>>::total_supply(self)211	}212213	fn account_balance(&self, account: T::CrossAccountId) -> u32 {214		<AccountBalance<T>>::get((self.id, account.as_sub()))215	}216217	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {218		if <TokenData<T>>::get((self.id, token))219			.map(|a| a.owner == account)220			.unwrap_or(false)221		{222			1223		} else {224			0225		}226	}227228	fn allowance(229		&self,230		sender: T::CrossAccountId,231		spender: T::CrossAccountId,232		token: TokenId,233	) -> u128 {234		if <TokenData<T>>::get((self.id, token))235			.map(|a| a.owner == sender)236			.unwrap_or(false)237		{238			0239		} else if <Allowance<T>>::get((self.id, token)) == Some(spender) {240			1241		} else {242			0243		}244	}245}
after · pallets/nonfungible/src/common.rs
1use core::marker::PhantomData;23use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};4use nft_data_structs::TokenId;5use pallet_common::{6	CommonCollectionOperations, CommonWeightInfo, account::CrossAccountId, with_weight,7};8use sp_runtime::DispatchError;9use sp_std::vec::Vec;1011use crate::{12	AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,13	SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,14};1516pub struct CommonWeights<T: Config>(PhantomData<T>);17impl<T: Config> CommonWeightInfo for CommonWeights<T> {18	fn create_item() -> Weight {19		<SelfWeightOf<T>>::create_item()20	}2122	fn create_multiple_items(amount: u32) -> Weight {23		<SelfWeightOf<T>>::create_multiple_items(amount)24	}2526	fn burn_item() -> Weight {27		<SelfWeightOf<T>>::burn_item()28	}2930	fn transfer() -> Weight {31		<SelfWeightOf<T>>::transfer()32	}3334	fn approve() -> Weight {35		<SelfWeightOf<T>>::approve()36	}3738	fn transfer_from() -> Weight {39		<SelfWeightOf<T>>::transfer_from()40	}4142	fn burn_from() -> Weight {43		044	}4546	fn set_variable_metadata(bytes: u32) -> Weight {47		<SelfWeightOf<T>>::set_variable_metadata(bytes)48	}49}5051fn map_create_data<T: Config>(52	data: nft_data_structs::CreateItemData,53	to: &T::CrossAccountId,54) -> Result<CreateItemData<T>, DispatchError> {55	match data {56		nft_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData {57			const_data: data.const_data,58			variable_data: data.variable_data,59			owner: to.clone(),60		}),61		_ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),62	}63}6465impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {66	fn create_item(67		&self,68		sender: T::CrossAccountId,69		to: T::CrossAccountId,70		data: nft_data_structs::CreateItemData,71	) -> DispatchResultWithPostInfo {72		with_weight(73			<Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),74			<CommonWeights<T>>::create_item(),75		)76	}7778	fn create_multiple_items(79		&self,80		sender: T::CrossAccountId,81		to: T::CrossAccountId,82		data: Vec<nft_data_structs::CreateItemData>,83	) -> DispatchResultWithPostInfo {84		let data = data85			.into_iter()86			.map(|d| map_create_data::<T>(d, &to))87			.collect::<Result<Vec<_>, DispatchError>>()?;8889		let amount = data.len();90		with_weight(91			<Pallet<T>>::create_multiple_items(self, &sender, data),92			<CommonWeights<T>>::create_multiple_items(amount as u32),93		)94	}9596	fn burn_item(97		&self,98		sender: T::CrossAccountId,99		token: TokenId,100		amount: u128,101	) -> DispatchResultWithPostInfo {102		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);103		if amount == 1 {104			with_weight(105				<Pallet<T>>::burn(&self, &sender, token),106				<CommonWeights<T>>::burn_item(),107			)108		} else {109			Ok(().into())110		}111	}112113	fn transfer(114		&self,115		from: T::CrossAccountId,116		to: T::CrossAccountId,117		token: TokenId,118		amount: u128,119	) -> DispatchResultWithPostInfo {120		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);121		if amount == 1 {122			with_weight(123				<Pallet<T>>::transfer(&self, &from, &to, token),124				<CommonWeights<T>>::transfer(),125			)126		} else {127			Ok(().into())128		}129	}130131	fn approve(132		&self,133		sender: T::CrossAccountId,134		spender: T::CrossAccountId,135		token: TokenId,136		amount: u128,137	) -> DispatchResultWithPostInfo {138		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);139140		with_weight(141			if amount == 1 {142				<Pallet<T>>::set_allowance(&self, &sender, token, Some(&spender))143			} else {144				<Pallet<T>>::set_allowance(&self, &sender, token, None)145			},146			<CommonWeights<T>>::approve(),147		)148	}149150	fn transfer_from(151		&self,152		sender: T::CrossAccountId,153		from: T::CrossAccountId,154		to: T::CrossAccountId,155		token: TokenId,156		amount: u128,157	) -> DispatchResultWithPostInfo {158		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);159160		if amount == 1 {161			with_weight(162				<Pallet<T>>::transfer_from(&self, &sender, &from, &to, token),163				<CommonWeights<T>>::transfer_from(),164			)165		} else {166			Ok(().into())167		}168	}169170	fn burn_from(171		&self,172		sender: T::CrossAccountId,173		from: T::CrossAccountId,174		token: TokenId,175		amount: u128,176	) -> DispatchResultWithPostInfo {177		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);178179		if amount == 1 {180			with_weight(181				<Pallet<T>>::burn_from(&self, &sender, &from, token),182				<CommonWeights<T>>::burn_from(),183			)184		} else {185			Ok(().into())186		}187	}188189	fn set_variable_metadata(190		&self,191		sender: T::CrossAccountId,192		token: TokenId,193		data: Vec<u8>,194	) -> DispatchResultWithPostInfo {195		let len = data.len();196		with_weight(197			<Pallet<T>>::set_variable_metadata(&self, &sender, token, data),198			<CommonWeights<T>>::set_variable_metadata(len as u32),199		)200	}201202	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {203		<Owned<T>>::iter_prefix((self.id, account.as_sub()))204			.map(|(id, _)| id)205			.collect()206	}207208	fn token_exists(&self, token: TokenId) -> bool {209		<Pallet<T>>::token_exists(self, token)210	}211212	fn last_token_id(&self) -> TokenId {213		TokenId(<TokensMinted<T>>::get(self.id))214	}215216	fn token_owner(&self, token: TokenId) -> T::CrossAccountId {217		<TokenData<T>>::get((self.id, token))218			.map(|t| t.owner)219			.unwrap_or_default()220	}221	fn const_metadata(&self, token: TokenId) -> Vec<u8> {222		<TokenData<T>>::get((self.id, token))223			.map(|t| t.const_data.clone())224			.unwrap_or_default()225	}226	fn variable_metadata(&self, token: TokenId) -> Vec<u8> {227		<TokenData<T>>::get((self.id, token))228			.map(|t| t.variable_data.clone())229			.unwrap_or_default()230	}231232	fn collection_tokens(&self) -> u32 {233		<Pallet<T>>::total_supply(self)234	}235236	fn account_balance(&self, account: T::CrossAccountId) -> u32 {237		<AccountBalance<T>>::get((self.id, account.as_sub()))238	}239240	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {241		if <TokenData<T>>::get((self.id, token))242			.map(|a| a.owner == account)243			.unwrap_or(false)244		{245			1246		} else {247			0248		}249	}250251	fn allowance(252		&self,253		sender: T::CrossAccountId,254		spender: T::CrossAccountId,255		token: TokenId,256	) -> u128 {257		if <TokenData<T>>::get((self.id, token))258			.map(|a| a.owner == sender)259			.unwrap_or(false)260		{261			0262		} else if <Allowance<T>>::get((self.id, token)) == Some(spender) {263			1264		} else {265			0266		}267	}268}
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -264,8 +264,7 @@
 
 #[solidity_interface(name = "ERC721UniqueExtensions")]
 impl<T: Config> NonfungibleHandle<T> {
-	#[solidity(rename_selector = "transfer")]
-	fn transfer_nft(
+	fn transfer(
 		&mut self,
 		caller: caller,
 		to: address,
@@ -280,6 +279,21 @@
 		Ok(())
 	}
 
+	fn burn_from(
+		&mut self,
+		caller: caller,
+		from: address,
+		token_id: uint256,
+		_value: value,
+	) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let from = T::CrossAccountId::from_eth(from);
+		let token = token_id.try_into()?;
+
+		<Pallet<T>>::burn_from(self, &caller, &from, token).map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
 	fn next_token_id(&self) -> Result<uint256> {
 		Ok(<TokensMinted<T>>::get(self.id)
 			.checked_add(1)
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -499,6 +499,32 @@
 		Ok(())
 	}
 
+	pub fn burn_from(
+		collection: &NonfungibleHandle<T>,
+		spender: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		token: TokenId,
+	) -> DispatchResult {
+		if spender == from {
+			return Self::burn(collection, from, token);
+		}
+		if collection.access == AccessMode::WhiteList {
+			// `from` checked in [`burn`]
+			collection.check_allowlist(spender)?;
+		}
+
+		if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {
+			ensure!(
+				collection.ignores_allowance(spender)?,
+				<CommonError<T>>::TokenValueNotEnough
+			);
+		}
+
+		// =========
+
+		Self::burn(collection, &from, token)
+	}
+
 	pub fn set_variable_metadata(
 		collection: &NonfungibleHandle<T>,
 		sender: &T::CrossAccountId,
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -59,6 +59,10 @@
 		)
 	}
 
+	fn burn_from() -> Weight {
+		0
+	}
+
 	fn set_variable_metadata(bytes: u32) -> Weight {
 		<SelfWeightOf<T>>::set_variable_metadata(bytes)
 	}
@@ -161,7 +165,20 @@
 	) -> DispatchResultWithPostInfo {
 		with_weight(
 			<Pallet<T>>::transfer_from(&self, &sender, &from, &to, token, amount),
-			<CommonWeights<T>>::approve(),
+			<CommonWeights<T>>::transfer_from(),
+		)
+	}
+
+	fn burn_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		with_weight(
+			<Pallet<T>>::burn_from(&self, &sender, &from, token, amount),
+			<CommonWeights<T>>::burn_from(),
 		)
 	}
 
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -537,6 +537,39 @@
 		Ok(())
 	}
 
+	pub fn burn_from(
+		collection: &RefungibleHandle<T>,
+		spender: &T::CrossAccountId,
+		from: &T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResult {
+		if spender == from {
+			return Self::burn(collection, from, token, amount);
+		}
+		if collection.access == AccessMode::WhiteList {
+			// `from` checked in [`burn`]
+			collection.check_allowlist(spender)?;
+		}
+
+		let allowance = <Allowance<T>>::get((collection.id, token, from.as_sub(), &spender))
+			.checked_sub(amount);
+		if allowance.is_none() {
+			ensure!(
+				collection.ignores_allowance(spender)?,
+				<CommonError<T>>::TokenValueNotEnough
+			);
+		}
+
+		// =========
+
+		Self::burn(collection, from, token, amount)?;
+		if let Some(allowance) = allowance {
+			Self::set_allowance_unchecked(collection, from, spender, token, allowance);
+		}
+		Ok(())
+	}
+
 	pub fn set_variable_metadata(
 		collection: &RefungibleHandle<T>,
 		sender: &T::CrossAccountId,