#![cfg_attr(not(feature = "std"), no_std)]
#![warn(unused_crate_dependencies)]
#![allow(clippy::too_many_arguments)]
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
#[cfg(test)]
mod mock;
pub mod runner;
#[cfg(test)]
mod tests;
pub mod weights;
pub use evm::{
Config as EvmConfig, Context, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed,
};
use hash_db::Hasher;
use impl_trait_for_tuples::impl_for_tuples;
use scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use frame_support::{
dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo},
storage::{child::KillStorageResult, KeyPrefixIterator},
traits::{
tokens::{
currency::Currency,
fungible::Inspect,
imbalance::{Imbalance, OnUnbalanced, SignedImbalance},
ExistenceRequirement, Fortitude, Preservation, WithdrawReasons,
},
FindAuthor, Get, Time,
},
weights::Weight,
};
use frame_system::RawOrigin;
use sp_core::{H160, H256, U256};
use sp_runtime::{
traits::{BadOrigin, NumberFor, Saturating, UniqueSaturatedInto, Zero},
AccountId32, DispatchErrorWithPostInfo,
};
use sp_std::{cmp::min, collections::btree_map::BTreeMap, vec::Vec};
use fp_account::AccountId20;
use fp_evm::GenesisAccount;
pub use fp_evm::{
Account, CallInfo, CreateInfo, ExecutionInfoV2 as ExecutionInfo, FeeCalculator,
IsPrecompileResult, LinearCostPrecompile, Log, Precompile, PrecompileFailure, PrecompileHandle,
PrecompileOutput, PrecompileResult, PrecompileSet, TransactionValidationError, Vicinity,
};
pub use self::{
pallet::*,
runner::{Runner, RunnerError},
weights::WeightInfo,
};
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::pallet]
#[pallet::without_storage_info]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::config]
pub trait Config: frame_system::Config {
type FeeCalculator: FeeCalculator;
type GasWeightMapping: GasWeightMapping;
type WeightPerGas: Get<Weight>;
type BlockHashMapping: BlockHashMapping;
type CallOrigin: EnsureAddressOrigin<Self::RuntimeOrigin>;
type WithdrawOrigin: EnsureAddressOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
type AddressMapping: AddressMapping<Self::AccountId>;
type Currency: Currency<Self::AccountId> + Inspect<Self::AccountId>;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type PrecompilesType: PrecompileSet;
type PrecompilesValue: Get<Self::PrecompilesType>;
type ChainId: Get<u64>;
type BlockGasLimit: Get<U256>;
type Runner: Runner<Self>;
type OnChargeTransaction: OnChargeEVMTransaction<Self>;
type OnCreate: OnCreate<Self>;
type FindAuthor: FindAuthor<H160>;
type GasLimitPovSizeRatio: Get<u64>;
type SuicideQuickClearLimit: Get<u32>;
type Timestamp: Time;
type WeightInfo: WeightInfo;
fn config() -> &'static EvmConfig {
&SHANGHAI_CONFIG
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::withdraw())]
pub fn withdraw(
origin: OriginFor<T>,
address: H160,
value: BalanceOf<T>,
) -> DispatchResult {
let destination = T::WithdrawOrigin::ensure_address_origin(&address, origin)?;
let address_account_id = T::AddressMapping::into_account_id(address);
T::Currency::transfer(
&address_account_id,
&destination,
value,
ExistenceRequirement::AllowDeath,
)?;
Ok(())
}
#[pallet::call_index(1)]
#[pallet::weight({
let without_base_extrinsic_weight = true;
T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
})]
pub fn call(
origin: OriginFor<T>,
source: H160,
target: H160,
input: Vec<u8>,
value: U256,
gas_limit: u64,
max_fee_per_gas: U256,
max_priority_fee_per_gas: Option<U256>,
nonce: Option<U256>,
access_list: Vec<(H160, Vec<H256>)>,
) -> DispatchResultWithPostInfo {
T::CallOrigin::ensure_address_origin(&source, origin)?;
let is_transactional = true;
let validate = true;
let info = match T::Runner::call(
source,
target,
input,
value,
gas_limit,
Some(max_fee_per_gas),
max_priority_fee_per_gas,
nonce,
access_list,
is_transactional,
validate,
None,
None,
T::config(),
) {
Ok(info) => info,
Err(e) => {
return Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(e.weight),
pays_fee: Pays::Yes,
},
error: e.error.into(),
})
}
};
match info.exit_reason {
ExitReason::Succeed(_) => {
Pallet::<T>::deposit_event(Event::<T>::Executed { address: target });
}
_ => {
Pallet::<T>::deposit_event(Event::<T>::ExecutedFailed { address: target });
}
};
Ok(PostDispatchInfo {
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.standard.unique_saturated_into(),
true,
);
if let Some(weight_info) = info.weight_info {
if let Some(proof_size_usage) = weight_info.proof_size_usage {
*gas_to_weight.proof_size_mut() = proof_size_usage;
}
}
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
#[pallet::call_index(2)]
#[pallet::weight({
let without_base_extrinsic_weight = true;
T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
})]
pub fn create(
origin: OriginFor<T>,
source: H160,
init: Vec<u8>,
value: U256,
gas_limit: u64,
max_fee_per_gas: U256,
max_priority_fee_per_gas: Option<U256>,
nonce: Option<U256>,
access_list: Vec<(H160, Vec<H256>)>,
) -> DispatchResultWithPostInfo {
T::CallOrigin::ensure_address_origin(&source, origin)?;
let is_transactional = true;
let validate = true;
let info = match T::Runner::create(
source,
init,
value,
gas_limit,
Some(max_fee_per_gas),
max_priority_fee_per_gas,
nonce,
access_list,
is_transactional,
validate,
None,
None,
T::config(),
) {
Ok(info) => info,
Err(e) => {
return Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(e.weight),
pays_fee: Pays::Yes,
},
error: e.error.into(),
})
}
};
match info {
CreateInfo {
exit_reason: ExitReason::Succeed(_),
value: create_address,
..
} => {
Pallet::<T>::deposit_event(Event::<T>::Created {
address: create_address,
});
}
CreateInfo {
exit_reason: _,
value: create_address,
..
} => {
Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
address: create_address,
});
}
}
Ok(PostDispatchInfo {
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.standard.unique_saturated_into(),
true,
);
if let Some(weight_info) = info.weight_info {
if let Some(proof_size_usage) = weight_info.proof_size_usage {
*gas_to_weight.proof_size_mut() = proof_size_usage;
}
}
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
#[pallet::call_index(3)]
#[pallet::weight({
let without_base_extrinsic_weight = true;
T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight)
})]
pub fn create2(
origin: OriginFor<T>,
source: H160,
init: Vec<u8>,
salt: H256,
value: U256,
gas_limit: u64,
max_fee_per_gas: U256,
max_priority_fee_per_gas: Option<U256>,
nonce: Option<U256>,
access_list: Vec<(H160, Vec<H256>)>,
) -> DispatchResultWithPostInfo {
T::CallOrigin::ensure_address_origin(&source, origin)?;
let is_transactional = true;
let validate = true;
let info = match T::Runner::create2(
source,
init,
salt,
value,
gas_limit,
Some(max_fee_per_gas),
max_priority_fee_per_gas,
nonce,
access_list,
is_transactional,
validate,
None,
None,
T::config(),
) {
Ok(info) => info,
Err(e) => {
return Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(e.weight),
pays_fee: Pays::Yes,
},
error: e.error.into(),
})
}
};
match info {
CreateInfo {
exit_reason: ExitReason::Succeed(_),
value: create_address,
..
} => {
Pallet::<T>::deposit_event(Event::<T>::Created {
address: create_address,
});
}
CreateInfo {
exit_reason: _,
value: create_address,
..
} => {
Pallet::<T>::deposit_event(Event::<T>::CreatedFailed {
address: create_address,
});
}
}
Ok(PostDispatchInfo {
actual_weight: {
let mut gas_to_weight = T::GasWeightMapping::gas_to_weight(
info.used_gas.standard.unique_saturated_into(),
true,
);
if let Some(weight_info) = info.weight_info {
if let Some(proof_size_usage) = weight_info.proof_size_usage {
*gas_to_weight.proof_size_mut() = proof_size_usage;
}
}
Some(gas_to_weight)
},
pays_fee: Pays::No,
})
}
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Log { log: Log },
Created { address: H160 },
CreatedFailed { address: H160 },
Executed { address: H160 },
ExecutedFailed { address: H160 },
}
#[pallet::error]
pub enum Error<T> {
BalanceLow,
FeeOverflow,
PaymentOverflow,
WithdrawFailed,
GasPriceTooLow,
InvalidNonce,
GasLimitTooLow,
GasLimitTooHigh,
InvalidChainId,
InvalidSignature,
Reentrancy,
TransactionMustComeFromEOA,
Undefined,
}
impl<T> From<TransactionValidationError> for Error<T> {
fn from(validation_error: TransactionValidationError) -> Self {
match validation_error {
TransactionValidationError::GasLimitTooLow => Error::<T>::GasLimitTooLow,
TransactionValidationError::GasLimitTooHigh => Error::<T>::GasLimitTooHigh,
TransactionValidationError::BalanceTooLow => Error::<T>::BalanceLow,
TransactionValidationError::TxNonceTooLow => Error::<T>::InvalidNonce,
TransactionValidationError::TxNonceTooHigh => Error::<T>::InvalidNonce,
TransactionValidationError::GasPriceTooLow => Error::<T>::GasPriceTooLow,
TransactionValidationError::PriorityFeeTooHigh => Error::<T>::GasPriceTooLow,
TransactionValidationError::InvalidFeeInput => Error::<T>::GasPriceTooLow,
TransactionValidationError::InvalidChainId => Error::<T>::InvalidChainId,
TransactionValidationError::InvalidSignature => Error::<T>::InvalidSignature,
TransactionValidationError::UnknownError => Error::<T>::Undefined,
}
}
}
#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T> {
pub accounts: BTreeMap<H160, GenesisAccount>,
#[serde(skip)]
pub _marker: PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T>
where
U256: UniqueSaturatedInto<BalanceOf<T>>,
{
fn build(&self) {
const MAX_ACCOUNT_NONCE: usize = 100;
for (address, account) in &self.accounts {
let account_id = T::AddressMapping::into_account_id(*address);
for _ in 0..min(
MAX_ACCOUNT_NONCE,
UniqueSaturatedInto::<usize>::unique_saturated_into(account.nonce),
) {
frame_system::Pallet::<T>::inc_account_nonce(&account_id);
}
let _ = T::Currency::deposit_creating(
&account_id,
account.balance.unique_saturated_into(),
);
Pallet::<T>::create_account(*address, account.code.clone());
for (index, value) in &account.storage {
<AccountStorages<T>>::insert(address, index, value);
}
}
}
}
#[pallet::storage]
pub type AccountCodes<T: Config> = StorageMap<_, Blake2_128Concat, H160, Vec<u8>, ValueQuery>;
#[pallet::storage]
pub type AccountCodesMetadata<T: Config> =
StorageMap<_, Blake2_128Concat, H160, CodeMetadata, OptionQuery>;
#[pallet::storage]
pub type AccountStorages<T: Config> =
StorageDoubleMap<_, Blake2_128Concat, H160, Blake2_128Concat, H256, H256, ValueQuery>;
#[pallet::storage]
pub type Suicided<T: Config> = StorageMap<_, Blake2_128Concat, H160, (), OptionQuery>;
}
pub type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
type NegativeImbalanceOf<C, T> =
<C as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
#[derive(
Debug,
Clone,
Copy,
Eq,
PartialEq,
Encode,
Decode,
TypeInfo,
MaxEncodedLen
)]
pub struct CodeMetadata {
pub size: u64,
pub hash: H256,
}
impl CodeMetadata {
fn from_code(code: &[u8]) -> Self {
let size = code.len() as u64;
let hash = H256::from(sp_io::hashing::keccak_256(code));
Self { size, hash }
}
}
pub trait EnsureAddressOrigin<OuterOrigin> {
type Success;
fn ensure_address_origin(
address: &H160,
origin: OuterOrigin,
) -> Result<Self::Success, BadOrigin> {
Self::try_address_origin(address, origin).map_err(|_| BadOrigin)
}
fn try_address_origin(
address: &H160,
origin: OuterOrigin,
) -> Result<Self::Success, OuterOrigin>;
}
pub struct EnsureAddressSame;
impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressSame
where
OuterOrigin: Into<Result<RawOrigin<H160>, OuterOrigin>> + From<RawOrigin<H160>>,
{
type Success = H160;
fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<H160, OuterOrigin> {
origin.into().and_then(|o| match o {
RawOrigin::Signed(who) if &who == address => Ok(who),
r => Err(OuterOrigin::from(r)),
})
}
}
pub struct EnsureAddressRoot<AccountId>(sp_std::marker::PhantomData<AccountId>);
impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressRoot<AccountId>
where
OuterOrigin: Into<Result<RawOrigin<AccountId>, OuterOrigin>> + From<RawOrigin<AccountId>>,
{
type Success = ();
fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<(), OuterOrigin> {
origin.into().and_then(|o| match o {
RawOrigin::Root => Ok(()),
r => Err(OuterOrigin::from(r)),
})
}
}
pub struct EnsureAddressNever<AccountId>(sp_std::marker::PhantomData<AccountId>);
impl<OuterOrigin, AccountId> EnsureAddressOrigin<OuterOrigin> for EnsureAddressNever<AccountId> {
type Success = AccountId;
fn try_address_origin(_address: &H160, origin: OuterOrigin) -> Result<AccountId, OuterOrigin> {
Err(origin)
}
}
pub struct EnsureAddressTruncated;
impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAddressTruncated
where
OuterOrigin: Into<Result<RawOrigin<AccountId32>, OuterOrigin>> + From<RawOrigin<AccountId32>>,
{
type Success = AccountId32;
fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId32, OuterOrigin> {
origin.into().and_then(|o| match o {
RawOrigin::Signed(who) if AsRef::<[u8; 32]>::as_ref(&who)[0..20] == address[0..20] => {
Ok(who)
}
r => Err(OuterOrigin::from(r)),
})
}
}
pub struct EnsureAccountId20;
impl<OuterOrigin> EnsureAddressOrigin<OuterOrigin> for EnsureAccountId20
where
OuterOrigin: Into<Result<RawOrigin<AccountId20>, OuterOrigin>> + From<RawOrigin<AccountId20>>,
{
type Success = AccountId20;
fn try_address_origin(address: &H160, origin: OuterOrigin) -> Result<AccountId20, OuterOrigin> {
let acc: AccountId20 = AccountId20::from(*address);
origin.into().and_then(|o| match o {
RawOrigin::Signed(who) if who == acc => Ok(who),
r => Err(OuterOrigin::from(r)),
})
}
}
pub trait AddressMapping<A> {
fn into_account_id(address: H160) -> A;
}
pub struct IdentityAddressMapping;
impl<T: From<H160>> AddressMapping<T> for IdentityAddressMapping {
fn into_account_id(address: H160) -> T {
address.into()
}
}
pub struct HashedAddressMapping<H>(sp_std::marker::PhantomData<H>);
impl<H: Hasher<Out = H256>> AddressMapping<AccountId32> for HashedAddressMapping<H> {
fn into_account_id(address: H160) -> AccountId32 {
let mut data = [0u8; 24];
data[0..4].copy_from_slice(b"evm:");
data[4..24].copy_from_slice(&address[..]);
let hash = H::hash(&data);
AccountId32::from(Into::<[u8; 32]>::into(hash))
}
}
pub trait BlockHashMapping {
fn block_hash(number: u32) -> H256;
}
pub struct SubstrateBlockHashMapping<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> BlockHashMapping for SubstrateBlockHashMapping<T> {
fn block_hash(number: u32) -> H256 {
let number = <NumberFor<T::Block>>::from(number);
H256::from_slice(frame_system::Pallet::<T>::block_hash(number).as_ref())
}
}
pub trait GasWeightMapping {
fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight;
fn weight_to_gas(weight: Weight) -> u64;
}
pub struct FixedGasWeightMapping<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> GasWeightMapping for FixedGasWeightMapping<T> {
fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight {
let mut weight = T::WeightPerGas::get().saturating_mul(gas);
if without_base_weight {
weight = weight.saturating_sub(
T::BlockWeights::get()
.get(frame_support::dispatch::DispatchClass::Normal)
.base_extrinsic,
);
}
let ratio = T::GasLimitPovSizeRatio::get();
if ratio > 0 {
let proof_size = gas.saturating_div(ratio);
*weight.proof_size_mut() = proof_size;
}
weight
}
fn weight_to_gas(weight: Weight) -> u64 {
weight.div(T::WeightPerGas::get().ref_time()).ref_time()
}
}
static SHANGHAI_CONFIG: EvmConfig = EvmConfig::shanghai();
impl<T: Config> Pallet<T> {
pub fn is_account_empty(address: &H160) -> bool {
let (account, _) = Self::account_basic(address);
let code_len = <AccountCodes<T>>::decode_len(address).unwrap_or(0);
account.nonce == U256::zero() && account.balance == U256::zero() && code_len == 0
}
pub fn is_account_suicided(address: &H160) -> bool {
<Suicided<T>>::contains_key(address)
}
pub fn iter_account_storages(address: &H160) -> KeyPrefixIterator<H256> {
<AccountStorages<T>>::iter_key_prefix(address)
}
pub fn remove_account_if_empty(address: &H160) {
if Self::is_account_empty(address) {
Self::remove_account(address);
}
}
pub fn remove_account(address: &H160) {
if <AccountCodes<T>>::contains_key(address) {
<Suicided<T>>::insert(address, ());
let account_id = T::AddressMapping::into_account_id(*address);
frame_system::Pallet::<T>::inc_account_nonce(&account_id);
}
<AccountCodes<T>>::remove(address);
<AccountCodesMetadata<T>>::remove(address);
if T::SuicideQuickClearLimit::get() > 0 {
#[allow(deprecated)]
let res = <AccountStorages<T>>::remove_prefix(address, Some(T::SuicideQuickClearLimit::get()));
match res {
KillStorageResult::AllRemoved(_) => {
<Suicided<T>>::remove(address);
let account_id = T::AddressMapping::into_account_id(*address);
let _ = frame_system::Pallet::<T>::dec_sufficients(&account_id);
}
KillStorageResult::SomeRemaining(_) => (),
}
}
}
pub fn create_account(address: H160, code: Vec<u8>) {
if <Suicided<T>>::contains_key(address) {
return;
}
if code.is_empty() {
return;
}
if !<AccountCodes<T>>::contains_key(address) {
let account_id = T::AddressMapping::into_account_id(address);
let _ = frame_system::Pallet::<T>::inc_sufficients(&account_id);
}
let meta = CodeMetadata::from_code(&code);
<AccountCodesMetadata<T>>::insert(address, meta);
<AccountCodes<T>>::insert(address, code);
}
pub fn account_code_metadata(address: H160) -> CodeMetadata {
if let Some(meta) = <AccountCodesMetadata<T>>::get(address) {
return meta;
}
let code = <AccountCodes<T>>::get(address);
if code.is_empty() {
const EMPTY_CODE_HASH: [u8; 32] = hex_literal::hex!(
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
);
return CodeMetadata {
size: 0,
hash: EMPTY_CODE_HASH.into(),
};
}
let meta = CodeMetadata::from_code(&code);
<AccountCodesMetadata<T>>::insert(address, meta);
meta
}
pub fn account_basic(address: &H160) -> (Account, frame_support::weights::Weight) {
let account_id = T::AddressMapping::into_account_id(*address);
let nonce = frame_system::Pallet::<T>::account_nonce(&account_id);
let balance =
T::Currency::reducible_balance(&account_id, Preservation::Preserve, Fortitude::Polite);
(
Account {
nonce: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(nonce)),
balance: U256::from(UniqueSaturatedInto::<u128>::unique_saturated_into(balance)),
},
T::DbWeight::get().reads(2),
)
}
pub fn find_author() -> H160 {
let digest = <frame_system::Pallet<T>>::digest();
let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
T::FindAuthor::find_author(pre_runtime_digests).unwrap_or_default()
}
}
pub trait OnChargeEVMTransaction<T: Config> {
type LiquidityInfo: Default;
fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>>;
fn correct_and_deposit_fee(
who: &H160,
corrected_fee: U256,
base_fee: U256,
already_withdrawn: Self::LiquidityInfo,
) -> Self::LiquidityInfo;
fn pay_priority_fee(tip: Self::LiquidityInfo);
}
pub struct EVMCurrencyAdapter<C, OU>(sp_std::marker::PhantomData<(C, OU)>);
impl<T, C, OU> OnChargeEVMTransaction<T> for EVMCurrencyAdapter<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>,
{
type LiquidityInfo = Option<NegativeImbalanceOf<C, T>>;
fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, Error<T>> {
if fee.is_zero() {
return Ok(None);
}
let account_id = T::AddressMapping::into_account_id(*who);
let imbalance = C::withdraw(
&account_id,
fee.unique_saturated_into(),
WithdrawReasons::FEE,
ExistenceRequirement::AllowDeath,
)
.map_err(|_| Error::<T>::BalanceLow)?;
Ok(Some(imbalance))
}
fn correct_and_deposit_fee(
who: &H160,
corrected_fee: U256,
base_fee: U256,
already_withdrawn: Self::LiquidityInfo,
) -> Self::LiquidityInfo {
if let Some(paid) = already_withdrawn {
let account_id = T::AddressMapping::into_account_id(*who);
let refund_amount = paid
.peek()
.saturating_sub(corrected_fee.unique_saturated_into());
let refund_imbalance = C::deposit_into_existing(&account_id, refund_amount)
.unwrap_or_else(|_| C::PositiveImbalance::zero());
let refund_imbalance = if C::minimum_balance().is_zero()
&& refund_amount > C::Balance::zero()
&& C::total_balance(&account_id).is_zero()
{
match C::make_free_balance_be(&account_id, refund_amount) {
SignedImbalance::Positive(p) => p,
_ => C::PositiveImbalance::zero(),
}
} else {
refund_imbalance
};
let adjusted_paid = paid
.offset(refund_imbalance)
.same()
.unwrap_or_else(|_| C::NegativeImbalance::zero());
let (base_fee, tip) = adjusted_paid.split(base_fee.unique_saturated_into());
OU::on_unbalanced(base_fee);
return Some(tip);
}
None
}
fn pay_priority_fee(tip: Self::LiquidityInfo) {
if let Some(tip) = tip {
let account_id = T::AddressMapping::into_account_id(<Pallet<T>>::find_author());
let _ = C::deposit_into_existing(&account_id, tip.peek());
}
}
}
impl<T> OnChargeEVMTransaction<T> for ()
where
T: Config,
<T::Currency as Currency<<T as frame_system::Config>::AccountId>>::PositiveImbalance:
Imbalance<<T::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance, Opposite = <T::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance>,
<T::Currency as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance:
Imbalance<<T::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance, Opposite = <T::Currency as Currency<<T as frame_system::Config>::AccountId>>::PositiveImbalance>,
U256: UniqueSaturatedInto<BalanceOf<T>>,
{
type LiquidityInfo = Option<NegativeImbalanceOf<T::Currency, T>>;
fn withdraw_fee(
who: &H160,
fee: U256,
) -> Result<Self::LiquidityInfo, Error<T>> {
EVMCurrencyAdapter::<<T as Config>::Currency, ()>::withdraw_fee(who, fee)
}
fn correct_and_deposit_fee(
who: &H160,
corrected_fee: U256,
base_fee: U256,
already_withdrawn: Self::LiquidityInfo,
) -> Self::LiquidityInfo {
<EVMCurrencyAdapter::<<T as Config>::Currency, ()> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(who, corrected_fee, base_fee, already_withdrawn)
}
fn pay_priority_fee(tip: Self::LiquidityInfo) {
<EVMCurrencyAdapter::<<T as Config>::Currency, ()> as OnChargeEVMTransaction<T>>::pay_priority_fee(tip);
}
}
pub trait OnCreate<T> {
fn on_create(owner: H160, contract: H160);
}
impl<T> OnCreate<T> for () {
fn on_create(_owner: H160, _contract: H160) {}
}
#[impl_for_tuples(1, 12)]
impl<T> OnCreate<T> for Tuple {
fn on_create(owner: H160, contract: H160) {
for_tuples!(#(
Tuple::on_create(owner, contract);
)*)
}
}