git.delta.rocks / unique-network / refs/commits / 71150e7c9b75

difftreelog

feat use new ethereum sponsoring interface

Yaroslav Bolyukin2023-10-02parent: #6ab76d3.patch.diff
in: master

2 files changed

modifiedpallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-transaction-payment/src/lib.rs
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -60,29 +60,62 @@
 	pub struct Pallet<T>(_);
 }
 
-/// Implements [`fp_evm::TransactionValidityHack`], which provides sponsor address to pallet-evm
-pub struct TransactionValidityHack<T: Config>(PhantomData<*const T>);
-impl<T: Config> fp_evm::TransactionValidityHack<T::CrossAccountId> for TransactionValidityHack<T> {
-	fn who_pays_fee(
-		origin: H160,
-		max_fee: U256,
-		reason: &WithdrawReason,
-	) -> Option<T::CrossAccountId> {
-		match reason {
-			WithdrawReason::Call { target, input } => {
-				let origin_sub = T::CrossAccountId::from_eth(origin);
-				let call_context = CallContext {
-					contract_address: *target,
-					input: input.clone(),
-					max_fee,
-				};
-				T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context)
-			}
-			_ => None,
+fn who_pays_fee<T: Config>(
+	origin: H160,
+	max_fee: U256,
+	reason: &WithdrawReason,
+) -> Option<T::CrossAccountId> {
+	match reason {
+		WithdrawReason::Call { target, input, .. } => {
+			let origin_sub = T::CrossAccountId::from_eth(origin);
+			let call_context = CallContext {
+				contract_address: *target,
+				input: input.clone(),
+				max_fee,
+			};
+			T::EvmSponsorshipHandler::get_sponsor(&origin_sub, &call_context)
 		}
+		_ => None,
 	}
 }
 
+fn get_sponsor<T: Config>(
+	source: H160,
+	max_fee_per_gas: Option<U256>,
+	gas_limit: u64,
+	reason: &WithdrawReason,
+	is_transactional: bool,
+	is_check: bool,
+) -> Option<T::CrossAccountId> {
+	let accept_gas_fee = |gas_fee| {
+		let (base_fee, _) = T::FeeCalculator::min_gas_price();
+		base_fee <= gas_fee && gas_fee <= base_fee * 21 / 10
+	};
+	let (max_fee_per_gas, may_sponsor) = match (max_fee_per_gas, is_transactional) {
+		(Some(max_fee_per_gas), _) => (max_fee_per_gas, accept_gas_fee(max_fee_per_gas)),
+		// Gas price check is skipped for non-transactional calls that don't
+		// define a `max_fee_per_gas` input.
+		(None, false) => (Default::default(), true),
+		_ => return None,
+	};
+
+	let max_fee = max_fee_per_gas.saturating_mul(gas_limit.into());
+
+	// #[cfg(feature = "debug-logging")]
+	// log::trace!(target: "sponsoring", "checking who will pay fee for {:?} {:?}", source, reason);
+	with_transaction(|| {
+		let result = may_sponsor
+			.then(|| who_pays_fee::<T>(source, max_fee, reason))
+			.flatten();
+		if is_check {
+			TransactionOutcome::Rollback(Ok::<_, DispatchError>(result))
+		} else {
+			TransactionOutcome::Commit(Ok(result))
+		}
+	})
+	.ok()
+	.flatten()
+}
 /// Implements sponsoring for evm calls performed from pallet-evm (via api.tx.ethereum.transact/api.tx.evm.call)
 pub struct BridgeSponsorshipHandler<T>(PhantomData<T>);
 impl<T, C> SponsorshipHandler<T::AccountId, C> for BridgeSponsorshipHandler<T>
@@ -127,3 +160,135 @@
 		}
 	}
 }
+
+/// Set transaction sponsor if available and enough balance.
+pub struct TransactionValidity<T>(PhantomData<T>);
+impl<T: Config> OnCheckEvmTransaction<T> for TransactionValidity<T> {
+	fn on_check_evm_transaction(
+		v: &mut CheckEvmTransaction,
+		origin: &T::CrossAccountId,
+	) -> Result<(), TransactionValidationError> {
+		let who = &v.who;
+		let max_fee_per_gas = Some(v.transaction_fee_input()?.0);
+		let gas_limit = v.transaction.gas_limit.low_u64();
+		let reason = if let Some(to) = v.transaction.to {
+			WithdrawReason::Call {
+				target: to,
+				input: v.transaction.input.clone(),
+				max_fee_per_gas,
+				gas_limit,
+				is_transactional: v.config.is_transactional,
+				is_check: true,
+			}
+		} else {
+			WithdrawReason::Create
+		};
+		let sponsor = get_sponsor::<T>(
+			*origin.as_eth(),
+			max_fee_per_gas,
+			gas_limit,
+			&reason,
+			v.config.is_transactional,
+			true,
+		)
+		.as_ref()
+		.map(pallet_evm::Pallet::<T>::account_basic_by_id)
+		.map(|v| v.0);
+		let fee = max_fee_per_gas
+			.unwrap()
+			.saturating_mul(v.transaction.gas_limit);
+		if let Some(sponsor) = sponsor.as_ref() {
+			if who.balance < v.transaction.value || sponsor.balance < fee {
+				return Err(TransactionValidationError::BalanceTooLow.into());
+			}
+		} else {
+			let total_payment = v.transaction.value.saturating_add(fee);
+			if who.balance < total_payment {
+				return Err(TransactionValidationError::BalanceTooLow.into());
+			}
+		}
+
+		let who = sponsor.unwrap_or_else(|| v.who.clone());
+		v.who.balance = who.balance;
+		Ok(())
+	}
+}
+
+/// Implements the transaction payment for a pallet implementing the `Currency`
+/// trait (eg. the pallet_balances) using an unbalance handler (implementing
+/// `OnUnbalanced`).
+/// Similar to `CurrencyAdapter` of `pallet_transaction_payment`
+pub struct WrappedEVMCurrencyAdapter<C, OU>(sp_std::marker::PhantomData<(C, OU)>);
+impl<T, C, OU> OnChargeEVMTransaction<T> for WrappedEVMCurrencyAdapter<C, OU>
+where
+	T: Config,
+	C: Currency<<T as frame_system::Config>::AccountId>,
+	C::PositiveImbalance: Imbalance<
+		<C as Currency<<T as frame_system::Config>::AccountId>>::Balance,
+		Opposite = C::NegativeImbalance,
+	>,
+	C::NegativeImbalance: Imbalance<
+		<C as Currency<<T as frame_system::Config>::AccountId>>::Balance,
+		Opposite = C::PositiveImbalance,
+	>,
+	OU: OnUnbalanced<NegativeImbalanceOf<C, T>>,
+	U256: UniqueSaturatedInto<<C as Currency<<T as frame_system::Config>::AccountId>>::Balance>,
+{
+	// Kept type as Option to satisfy bound of Default
+	type LiquidityInfo = (Option<NegativeImbalanceOf<C, T>>, Option<T::CrossAccountId>);
+
+	fn withdraw_fee(
+		who: &T::CrossAccountId,
+		reason: WithdrawReason,
+		fee: U256,
+	) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
+		let sponsor = match reason {
+			WithdrawReason::Call {
+				max_fee_per_gas,
+				gas_limit,
+				is_transactional,
+				is_check,
+				..
+			} => get_sponsor::<T>(
+				*who.as_eth(),
+				max_fee_per_gas,
+				gas_limit,
+				&reason,
+				is_transactional,
+				is_check,
+			),
+			_ => None,
+		};
+
+		let who = sponsor.as_ref().unwrap_or(who);
+		<pallet_evm::EVMCurrencyAdapter<C, OU> as OnChargeEVMTransaction<T>>::withdraw_fee(
+			who, reason, fee,
+		)
+		.map(|li| (li, sponsor))
+	}
+
+	fn correct_and_deposit_fee(
+		who: &T::CrossAccountId,
+		corrected_fee: U256,
+		base_fee: U256,
+		already_withdrawn: Self::LiquidityInfo,
+	) -> Self::LiquidityInfo {
+		let (already_withdrawn, sponsor) = already_withdrawn;
+		let who = sponsor.as_ref().unwrap_or(who);
+		(
+			<pallet_evm::EVMCurrencyAdapter<C, OU> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
+				who,
+				corrected_fee,
+				base_fee,
+				already_withdrawn,
+			),
+			None
+		)
+	}
+
+	fn pay_priority_fee(tip: Self::LiquidityInfo) {
+		<pallet_evm::EVMCurrencyAdapter<C, OU> as OnChargeEVMTransaction<T>>::pay_priority_fee(
+			tip.0,
+		)
+	}
+}
modifiedruntime/common/config/ethereum.rsdiffbeforeafterboth
89 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;89 type OnCreate = pallet_evm_contract_helpers::HelpersOnCreate<Self>;
90 type ChainId = ChainId;90 type ChainId = ChainId;
91 type Runner = pallet_evm::runner::stack::Runner<Self>;91 type Runner = pallet_evm::runner::stack::Runner<Self>;
92 type OnChargeTransaction = pallet_evm::EVMCurrencyAdapter<Balances, DealWithFees>;92 type OnChargeTransaction =
93 type TransactionValidityHack = pallet_evm_transaction_payment::TransactionValidityHack<Self>;93 pallet_evm_transaction_payment::WrappedEVMCurrencyAdapter<Balances, DealWithFees>;
94 type FindAuthor = EthereumFindAuthor<Aura>;94 type FindAuthor = EthereumFindAuthor<Aura>;
95 type Timestamp = crate::Timestamp;95 type Timestamp = crate::Timestamp;
96 type WeightInfo = pallet_evm::weights::SubstrateWeight<Self>;96 type WeightInfo = pallet_evm::weights::SubstrateWeight<Self>;
97 type GasLimitPovSizeRatio = ProofSizePerGas;97 type GasLimitPovSizeRatio = ProofSizePerGas;
98 type OnCheckEvmTransaction = pallet_evm_transaction_payment::TransactionValidity<Self>;
98}99}
99100
100impl pallet_evm_migration::Config for Runtime {101impl pallet_evm_migration::Config for Runtime {