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
before · pallets/nft/src/eth/sponsoring.rs
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};4use evm_coder::{Call, abi::AbiReader};5use frame_support::{6	storage::{StorageDoubleMap},7};8use pallet_common::eth::map_eth_to_id;9use sp_core::H160;10use sp_std::prelude::*;11use up_sponsorship::SponsorshipHandler;12use core::marker::PhantomData;13use core::convert::TryInto;14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};15use pallet_common::{16	CollectionById,17	account::{CrossAccountId, EvmBackwardsAddressMapping},18};1920use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};2223struct AnyError;2425fn try_sponsor<T: Config>(26	caller: &H160,27	collection_id: CollectionId,28	collection: &Collection<T>,29	call: &[u8],30) -> Result<(), AnyError> {31	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;32	match &collection.mode {33		crate::CollectionMode::NFT => {34			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)35				.map_err(|_| AnyError)?36				.ok_or(AnyError)?;37			match call {38				UniqueNFTCall::ERC721UniqueExtensions(39					ERC721UniqueExtensionsCall::TransferNft { token_id, .. },40				)41				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {42					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;43					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;44					let collection_limits = &collection.limits;45					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {46						collection_limits.sponsor_transfer_timeout47					} else {48						NFT_SPONSOR_TRANSFER_TIMEOUT49					};5051					let mut sponsor = true;52					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {53						let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);54						let limit_time = last_tx_block + limit.into();55						if block_number <= limit_time {56							sponsor = false;57						}58					}59					if sponsor {60						<NftTransferBasket<T>>::insert(collection_id, token_id, block_number);61						return Ok(());62					}63				}64				_ => {}65			}66		}67		crate::CollectionMode::Fungible(_) => {68			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)69				.map_err(|_| AnyError)?70				.ok_or(AnyError)?;71			#[allow(clippy::single_match)]72			match call {73				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {74					let who = T::CrossAccountId::from_eth(*caller);75					let collection_limits = &collection.limits;76					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {77						collection_limits.sponsor_transfer_timeout78					} else {79						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT80					};8182					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;83					let mut sponsored = true;84					if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {85						let last_tx_block =86							<FungibleTransferBasket<T>>::get(collection_id, who.as_sub());87						let limit_time = last_tx_block + limit.into();88						if block_number <= limit_time {89							sponsored = false;90						}91					}92					if sponsored {93						<FungibleTransferBasket<T>>::insert(94							collection_id,95							who.as_sub(),96							block_number,97						);98						return Ok(());99					}100				}101				_ => {}102			}103		}104		_ => {}105	}106	Err(AnyError)107}108109pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);110impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {111	fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {112		if let Some(collection_id) = map_eth_to_id(&call.0) {113			if let Some(collection) = <CollectionById<T>>::get(collection_id) {114				if !collection.sponsorship.confirmed() {115					return None;116				}117				if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {118					return collection119						.sponsorship120						.sponsor()121						.cloned()122						.map(T::EvmBackwardsAddressMapping::from_account_id);123				}124			}125		}126		None127	}128}
after · pallets/nft/src/eth/sponsoring.rs
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};4use evm_coder::{Call, abi::AbiReader};5use frame_support::{6	storage::{StorageDoubleMap},7};8use pallet_common::eth::map_eth_to_id;9use sp_core::H160;10use sp_std::prelude::*;11use up_sponsorship::SponsorshipHandler;12use core::marker::PhantomData;13use core::convert::TryInto;14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};15use pallet_common::{16	CollectionById,17	account::{CrossAccountId, EvmBackwardsAddressMapping},18};1920use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};2223struct AnyError;2425fn try_sponsor<T: Config>(26	caller: &H160,27	collection_id: CollectionId,28	collection: &Collection<T>,29	call: &[u8],30) -> Result<(), AnyError> {31	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;32	match &collection.mode {33		crate::CollectionMode::NFT => {34			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)35				.map_err(|_| AnyError)?36				.ok_or(AnyError)?;37			match call {38				UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {39					token_id,40					..41				})42				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {43					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;44					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;45					let collection_limits = &collection.limits;46					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {47						collection_limits.sponsor_transfer_timeout48					} else {49						NFT_SPONSOR_TRANSFER_TIMEOUT50					};5152					let mut sponsor = true;53					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {54						let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);55						let limit_time = last_tx_block + limit.into();56						if block_number <= limit_time {57							sponsor = false;58						}59					}60					if sponsor {61						<NftTransferBasket<T>>::insert(collection_id, token_id, block_number);62						return Ok(());63					}64				}65				_ => {}66			}67		}68		crate::CollectionMode::Fungible(_) => {69			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)70				.map_err(|_| AnyError)?71				.ok_or(AnyError)?;72			#[allow(clippy::single_match)]73			match call {74				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {75					let who = T::CrossAccountId::from_eth(*caller);76					let collection_limits = &collection.limits;77					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {78						collection_limits.sponsor_transfer_timeout79					} else {80						FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT81					};8283					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;84					let mut sponsored = true;85					if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {86						let last_tx_block =87							<FungibleTransferBasket<T>>::get(collection_id, who.as_sub());88						let limit_time = last_tx_block + limit.into();89						if block_number <= limit_time {90							sponsored = false;91						}92					}93					if sponsored {94						<FungibleTransferBasket<T>>::insert(95							collection_id,96							who.as_sub(),97							block_number,98						);99						return Ok(());100					}101				}102				_ => {}103			}104		}105		_ => {}106	}107	Err(AnyError)108}109110pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);111impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {112	fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {113		if let Some(collection_id) = map_eth_to_id(&call.0) {114			if let Some(collection) = <CollectionById<T>>::get(collection_id) {115				if !collection.sponsorship.confirmed() {116					return None;117				}118				if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {119					return collection120						.sponsorship121						.sponsor()122						.cloned()123						.map(T::EvmBackwardsAddressMapping::from_account_id);124				}125			}126		}127		None128	}129}
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
--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -39,6 +39,10 @@
 		<SelfWeightOf<T>>::transfer_from()
 	}
 
+	fn burn_from() -> Weight {
+		0
+	}
+
 	fn set_variable_metadata(bytes: u32) -> Weight {
 		<SelfWeightOf<T>>::set_variable_metadata(bytes)
 	}
@@ -163,6 +167,25 @@
 		}
 	}
 
+	fn burn_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+		if amount == 1 {
+			with_weight(
+				<Pallet<T>>::burn_from(&self, &sender, &from, token),
+				<CommonWeights<T>>::burn_from(),
+			)
+		} else {
+			Ok(().into())
+		}
+	}
+
 	fn set_variable_metadata(
 		&self,
 		sender: T::CrossAccountId,
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,