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

difftreelog

refactor extract evm transaction payment

Yaroslav Bolyukin2021-07-27parent: #60ea0ef.patch.diff
in: master

3 files changed

addedpallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-transaction-payment/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "pallet-evm-transaction-payment"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+frame-support = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+frame-system = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-runtime = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-std = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-io = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+sp-core = { default-features = false, version = '3.0.0', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.8' }
+pallet-evm = { default-features = false, version = "5.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+fp-evm = { default-features = false, version = "2.0.0", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+pallet-ethereum = { default-features = false, version = "3.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
+up-sponsorship = { default-features = false, path = '../../primitives/sponsorship' }
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[features]
+default = ["std"]
+std = [
+    "frame-support/std",
+    "frame-system/std",
+    "sp-runtime/std",
+    "sp-std/std",
+    "sp-io/std",
+    "sp-core/std",
+    "pallet-evm/std",
+    "pallet-ethereum/std",
+    "fp-evm/std",
+    "up-sponsorship/std",
+]
addedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -0,0 +1,98 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+#[frame_support::pallet]
+pub mod pallet {
+	use core::marker::PhantomData;
+	use frame_support::traits::Currency;
+	use pallet_evm::EVMCurrencyAdapter;
+	use fp_evm::WithdrawReason;
+	use sp_core::{H160, U256};
+	use sp_runtime::TransactionOutcome;
+	use up_sponsorship::SponsorshipHandler;
+	use sp_std::vec::Vec;
+
+	type NegativeImbalanceOf<C, T> =
+		<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		type SponsorshipHandler: SponsorshipHandler<H160, (H160, Vec<u8>)>;
+		type Currency: Currency<Self::AccountId>;
+	}
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	pub struct ChargeEvmLiquidityInfo<T>
+	where
+		T: Config,
+		T: pallet_evm::Config,
+	{
+		who: H160,
+		negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,
+	}
+
+	pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
+	impl<T: Config> fp_evm::TransactionValidityHack for TransactionValidityHack<T> {
+		fn who_pays_fee(origin: H160, reason: &WithdrawReason) -> Option<H160> {
+			match reason {
+				WithdrawReason::Call { target, input } => {
+					// This method is only used for checking, we shouldn't touch storage in it
+					frame_support::storage::with_transaction(|| {
+						TransactionOutcome::Rollback(T::SponsorshipHandler::get_sponsor(
+							&origin,
+							&(*target, input.clone()),
+						))
+					})
+				}
+				_ => None,
+			}
+		}
+	}
+
+	pub struct OnChargeTransaction<T: Config>(PhantomData<*const T>);
+	impl<T> pallet_evm::OnChargeEVMTransaction<T> for OnChargeTransaction<T>
+	where
+		T: Config,
+		T: pallet_evm::Config,
+	{
+		type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;
+
+		fn withdraw_fee(
+			who: &H160,
+			reason: WithdrawReason,
+			fee: U256,
+		) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
+			let mut who_pays_fee = *who;
+			if let WithdrawReason::Call { target, input } = &reason {
+				who_pays_fee = T::SponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
+					.unwrap_or(who_pays_fee);
+			}
+			let negative_imbalance =
+				EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(
+					&who_pays_fee,
+					reason,
+					fee,
+				)?;
+			Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {
+				who: who_pays_fee,
+				negative_imbalance: i,
+			}))
+		}
+
+		fn correct_and_deposit_fee(
+			who: &H160,
+			corrected_fee: U256,
+			already_withdrawn: Self::LiquidityInfo,
+		) -> core::result::Result<(), pallet_evm::Error<T>> {
+			EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(
+				&already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),
+				corrected_fee,
+				already_withdrawn.map(|e| e.negative_imbalance),
+			)
+		}
+	}
+}
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
before · pallets/nft/src/eth/sponsoring.rs
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{4	Collection, ChainLimit, CollectionById, Config, NftTransferBasket, FungibleTransferBasket,5	eth::account::EvmBackwardsAddressMapping,6};7use evm_coder::abi::AbiReader;8use frame_support::{9	storage::{StorageMap, StorageDoubleMap, StorageValue},10	traits::Currency,11};12use pallet_evm::{EVMCurrencyAdapter, WithdrawReason};13use sp_core::{H160, U256};14use sp_std::prelude::*;15use super::{16	account::CrossAccountId,17	erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},18};19use core::convert::TryInto;2021type NegativeImbalanceOf<C, T> =22	<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;2324pub struct ChargeEvmTransaction;25pub struct ChargeEvmLiquidityInfo<T>26where27	T: Config,28	T: pallet_evm::Config,29{30	who: H160,31	negative_imbalance: NegativeImbalanceOf<<T as Config>::Currency, T>,32}3334struct AnyError;3536fn try_sponsor<T: Config>(37	caller: &H160,38	collection_id: u32,39	collection: &Collection<T>,40	call: &[u8],41) -> Result<(), AnyError> {42	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;43	match &collection.mode {44		crate::CollectionMode::NFT => {45			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)46				.map_err(|_| AnyError)?47				.ok_or(AnyError)?;48			match call {49				UniqueNFTCall::ERC721UniqueExtensions(ERC721UniqueExtensionsCall::Transfer {50					token_id,51					..52				})53				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {54					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;55					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;56					let collection_limits = &collection.limits;57					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {58						collection_limits.sponsor_transfer_timeout59					} else {60						ChainLimit::get().nft_sponsor_transfer_timeout61					};6263					let mut sponsor = true;64					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {65						let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);66						let limit_time = last_tx_block + limit.into();67						if block_number <= limit_time {68							sponsor = false;69						}70					}71					if sponsor {72						<NftTransferBasket<T>>::insert(collection_id, token_id, block_number);73						return Ok(());74					}75				}76				_ => {}77			}78		}79		crate::CollectionMode::Fungible(_) => {80			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)81				.map_err(|_| AnyError)?82				.ok_or(AnyError)?;83			#[allow(clippy::single_match)]84			match call {85				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {86					let who = T::CrossAccountId::from_eth(*caller);87					let collection_limits = &collection.limits;88					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {89						collection_limits.sponsor_transfer_timeout90					} else {91						ChainLimit::get().fungible_sponsor_transfer_timeout92					};9394					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;95					let mut sponsored = true;96					if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {97						let last_tx_block =98							<FungibleTransferBasket<T>>::get(collection_id, who.as_sub());99						let limit_time = last_tx_block + limit.into();100						if block_number <= limit_time {101							sponsored = false;102						}103					}104					if sponsored {105						<FungibleTransferBasket<T>>::insert(106							collection_id,107							who.as_sub(),108							block_number,109						);110						return Ok(());111					}112				}113				_ => {}114			}115		}116		_ => {}117	}118	Err(AnyError)119}120121impl<T> pallet_evm::OnChargeEVMTransaction<T> for ChargeEvmTransaction122where123	T: Config,124	T: pallet_evm::Config,125{126	type LiquidityInfo = Option<ChargeEvmLiquidityInfo<T>>;127128	fn withdraw_fee(129		who: &H160,130		reason: WithdrawReason,131		fee: U256,132	) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {133		let mut who_pays_fee = *who;134		if let WithdrawReason::Call { target, input } = &reason {135			if let Some(collection_id) = crate::eth::map_eth_to_id(target) {136				if let Some(collection) = <CollectionById<T>>::get(collection_id) {137					if let Some(sponsor) = collection.sponsorship.sponsor() {138						if try_sponsor(who, collection_id, &collection, input).is_ok() {139							who_pays_fee =140								T::EvmBackwardsAddressMapping::from_account_id(sponsor.clone());141						}142					}143				}144			}145		}146147		// TODO: Pay fee from substrate address148		let negative_imbalance = EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(149			&who_pays_fee,150			reason,151			fee,152		)?;153		Ok(negative_imbalance.map(|i| ChargeEvmLiquidityInfo {154			who: who_pays_fee,155			negative_imbalance: i,156		}))157	}158159	fn correct_and_deposit_fee(160		who: &H160,161		corrected_fee: U256,162		already_withdrawn: Self::LiquidityInfo,163	) -> Result<(), pallet_evm::Error<T>> {164		EVMCurrencyAdapter::<<T as Config>::Currency, ()>::correct_and_deposit_fee(165			&already_withdrawn.as_ref().map(|e| e.who).unwrap_or(*who),166			corrected_fee,167			already_withdrawn.map(|e| e.negative_imbalance),168		)169	}170}
after · pallets/nft/src/eth/sponsoring.rs
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction23use crate::{4	ChainLimit, Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,5	eth::{account::EvmBackwardsAddressMapping, map_eth_to_id},6};7use evm_coder::{Call, abi::AbiReader};8use frame_support::{9	storage::{StorageMap, StorageDoubleMap, StorageValue},10};11use sp_core::H160;12use sp_std::prelude::*;13use up_sponsorship::SponsorshipHandler;14use super::{15	account::CrossAccountId,16	erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},17};18use core::convert::TryInto;19use core::marker::PhantomData;2021struct AnyError;2223fn try_sponsor<T: Config>(24	caller: &H160,25	collection_id: u32,26	collection: &Collection<T>,27	call: &[u8],28) -> Result<(), AnyError> {29	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;30	match &collection.mode {31		crate::CollectionMode::NFT => {32			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)33				.map_err(|_| AnyError)?34				.ok_or(AnyError)?;35			match call {36				UniqueNFTCall::ERC721UniqueExtensions(37					ERC721UniqueExtensionsCall::TransferNft { token_id, .. },38				)39				| UniqueNFTCall::ERC721(ERC721Call::TransferFrom { token_id, .. }) => {40					let token_id: u32 = token_id.try_into().map_err(|_| AnyError)?;41					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;42					let collection_limits = &collection.limits;43					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {44						collection_limits.sponsor_transfer_timeout45					} else {46						ChainLimit::get().nft_sponsor_transfer_timeout47					};4849					let mut sponsor = true;50					if <NftTransferBasket<T>>::contains_key(collection_id, token_id) {51						let last_tx_block = <NftTransferBasket<T>>::get(collection_id, token_id);52						let limit_time = last_tx_block + limit.into();53						if block_number <= limit_time {54							sponsor = false;55						}56					}57					if sponsor {58						<NftTransferBasket<T>>::insert(collection_id, token_id, block_number);59						return Ok(());60					}61				}62				_ => {}63			}64		}65		crate::CollectionMode::Fungible(_) => {66			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)67				.map_err(|_| AnyError)?68				.ok_or(AnyError)?;69			#[allow(clippy::single_match)]70			match call {71				UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {72					let who = T::CrossAccountId::from_eth(*caller);73					let collection_limits = &collection.limits;74					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {75						collection_limits.sponsor_transfer_timeout76					} else {77						ChainLimit::get().fungible_sponsor_transfer_timeout78					};7980					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;81					let mut sponsored = true;82					if <FungibleTransferBasket<T>>::contains_key(collection_id, who.as_sub()) {83						let last_tx_block =84							<FungibleTransferBasket<T>>::get(collection_id, who.as_sub());85						let limit_time = last_tx_block + limit.into();86						if block_number <= limit_time {87							sponsored = false;88						}89					}90					if sponsored {91						<FungibleTransferBasket<T>>::insert(92							collection_id,93							who.as_sub(),94							block_number,95						);96						return Ok(());97					}98				}99				_ => {}100			}101		}102		_ => {}103	}104	Err(AnyError)105}106107pub struct NftEthSponsorshipHandler<T: Config>(PhantomData<*const T>);108impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for NftEthSponsorshipHandler<T> {109	fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {110		if let Some(collection_id) = map_eth_to_id(&call.0) {111			if let Some(collection) = <CollectionById<T>>::get(collection_id) {112				if !collection.sponsorship.confirmed() {113					return None;114				}115				if try_sponsor(who, collection_id, &collection, &call.1).is_ok() {116					return collection117						.sponsorship118						.sponsor()119						.cloned()120						.map(T::EvmBackwardsAddressMapping::from_account_id);121				}122			}123		}124		None125	}126}