difftreelog
feat use new ethereum sponsoring interface
in: master
2 files changed
pallets/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,
+ )
+ }
+}
runtime/common/config/ethereum.rsdiffbeforeafterboth89 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}99100100impl pallet_evm_migration::Config for Runtime {101impl pallet_evm_migration::Config for Runtime {