astar_primitives/
evm.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19use crate::{AccountId, AssetId};
20
21use fp_evm::AccountProvider;
22use frame_support::{
23    ensure,
24    traits::{
25        fungible::{Balanced, Credit},
26        tokens::{fungible::Inspect, imbalance::OnUnbalanced},
27    },
28};
29use pallet_evm::OnChargeEVMTransaction;
30pub use sp_core::{H160, H256, U256};
31use sp_runtime::traits::UniqueSaturatedInto;
32use sp_std::marker::PhantomData;
33
34use pallet_assets::AssetsCallback;
35use pallet_evm_precompile_assets_erc20::AddressToAssetId;
36
37pub type EvmAddress = H160;
38
39/// Revert opt code. It's inserted at the precompile addresses, to make them functional in EVM.
40pub const EVM_REVERT_CODE: &[u8] = &[0x60, 0x00, 0x60, 0x00, 0xfd];
41
42/// Maximum gas allowed per transaction (EIP-7825). Substrate enforces a maximum of 65% of the block
43/// gas limit (39 million gas for Astar), so we enforce a cap close to that value.
44pub const TX_MAX_GAS_LIMIT: u64 = 35_000_000;
45
46/// Handler for automatic revert code registration.
47///
48/// When an asset is created, it automatically becomes available to the EVM via an `ERC20-like` interface.
49/// In order for the precompile to work, dedicated asset address needs to have the revert code registered, otherwise the call will fail.
50///
51/// It is important to note that if the dedicated asset EVM address is already taken, asset creation should fail.
52/// After asset has been destroyed, it is also safe to remove the revert code and free the address for future usage.
53pub struct EvmRevertCodeHandler<A, R>(PhantomData<(A, R)>);
54impl<A, R> AssetsCallback<AssetId, AccountId> for EvmRevertCodeHandler<A, R>
55where
56    A: AddressToAssetId<AssetId>,
57    R: pallet_evm::Config,
58{
59    fn created(id: &AssetId, _: &AccountId) -> Result<(), ()> {
60        let address = A::asset_id_to_address(*id);
61        // In case of collision, we need to cancel the asset creation.
62        ensure!(!pallet_evm::AccountCodes::<R>::contains_key(&address), ());
63        pallet_evm::AccountCodes::<R>::insert(address, EVM_REVERT_CODE.to_vec());
64        Ok(())
65    }
66
67    fn destroyed(id: &AssetId) -> Result<(), ()> {
68        let address = A::asset_id_to_address(*id);
69        pallet_evm::AccountCodes::<R>::remove(address);
70        Ok(())
71    }
72}
73
74/// Wrapper around the `EvmFungibleAdapter` from the `pallet-evm`.
75///
76/// While it provides most of the functionality we need,
77/// it doesn't allow the tip to be deposited into an arbitrary account.
78/// This adapter allows us to do that.
79///
80/// Two separate `OnUnbalanced` handers are used:
81/// - `UOF` for the fee
82/// - `OUT` for the tip
83pub struct EVMFungibleAdapterWrapper<F, FeeHandler, TipHandler>(
84    core::marker::PhantomData<(F, FeeHandler, TipHandler)>,
85);
86impl<T, F, FeeHandler, TipHandler> OnChargeEVMTransaction<T>
87    for EVMFungibleAdapterWrapper<F, FeeHandler, TipHandler>
88where
89    T: pallet_evm::Config,
90    F: Balanced<<T::AccountProvider as AccountProvider>::AccountId>,
91    FeeHandler: OnUnbalanced<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>,
92    TipHandler: OnUnbalanced<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>,
93    U256: UniqueSaturatedInto<
94        <F as Inspect<<T::AccountProvider as AccountProvider>::AccountId>>::Balance,
95    >,
96{
97    // Kept type as Option to satisfy bound of Default
98    type LiquidityInfo = Option<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>;
99
100    fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
101        pallet_evm::EVMFungibleAdapter::<F, FeeHandler>::withdraw_fee(who, fee)
102    }
103
104    fn can_withdraw(who: &H160, amount: U256) -> Result<(), pallet_evm::Error<T>> {
105        pallet_evm::EVMFungibleAdapter::<F, FeeHandler>::can_withdraw(who, amount)
106    }
107
108    fn correct_and_deposit_fee(
109        who: &H160,
110        corrected_fee: U256,
111        base_fee: U256,
112        already_withdrawn: Self::LiquidityInfo,
113    ) -> Self::LiquidityInfo {
114        <pallet_evm::EVMFungibleAdapter::<F, FeeHandler> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
115            who,
116            corrected_fee,
117            base_fee,
118            already_withdrawn,
119        )
120    }
121
122    fn pay_priority_fee(tip: Self::LiquidityInfo) {
123        if let Some(tip) = tip {
124            TipHandler::on_unbalanceds(Some(tip).into_iter());
125        }
126    }
127}