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/// Handler for automatic revert code registration.
43///
44/// When an asset is created, it automatically becomes available to the EVM via an `ERC20-like` interface.
45/// In order for the precompile to work, dedicated asset address needs to have the revert code registered, otherwise the call will fail.
46///
47/// It is important to note that if the dedicated asset EVM address is already taken, asset creation should fail.
48/// After asset has been destroyed, it is also safe to remove the revert code and free the address for future usage.
49pub struct EvmRevertCodeHandler<A, R>(PhantomData<(A, R)>);
50impl<A, R> AssetsCallback<AssetId, AccountId> for EvmRevertCodeHandler<A, R>
51where
52    A: AddressToAssetId<AssetId>,
53    R: pallet_evm::Config,
54{
55    fn created(id: &AssetId, _: &AccountId) -> Result<(), ()> {
56        let address = A::asset_id_to_address(*id);
57        // In case of collision, we need to cancel the asset creation.
58        ensure!(!pallet_evm::AccountCodes::<R>::contains_key(&address), ());
59        pallet_evm::AccountCodes::<R>::insert(address, EVM_REVERT_CODE.to_vec());
60        Ok(())
61    }
62
63    fn destroyed(id: &AssetId) -> Result<(), ()> {
64        let address = A::asset_id_to_address(*id);
65        pallet_evm::AccountCodes::<R>::remove(address);
66        Ok(())
67    }
68}
69
70/// Wrapper around the `EvmFungibleAdapter` from the `pallet-evm`.
71///
72/// While it provides most of the functionality we need,
73/// it doesn't allow the tip to be deposited into an arbitrary account.
74/// This adapter allows us to do that.
75///
76/// Two separate `OnUnbalanced` handers are used:
77/// - `UOF` for the fee
78/// - `OUT` for the tip
79pub struct EVMFungibleAdapterWrapper<F, FeeHandler, TipHandler>(
80    core::marker::PhantomData<(F, FeeHandler, TipHandler)>,
81);
82impl<T, F, FeeHandler, TipHandler> OnChargeEVMTransaction<T>
83    for EVMFungibleAdapterWrapper<F, FeeHandler, TipHandler>
84where
85    T: pallet_evm::Config,
86    F: Balanced<<T::AccountProvider as AccountProvider>::AccountId>,
87    FeeHandler: OnUnbalanced<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>,
88    TipHandler: OnUnbalanced<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>,
89    U256: UniqueSaturatedInto<
90        <F as Inspect<<T::AccountProvider as AccountProvider>::AccountId>>::Balance,
91    >,
92{
93    // Kept type as Option to satisfy bound of Default
94    type LiquidityInfo = Option<Credit<<T::AccountProvider as AccountProvider>::AccountId, F>>;
95
96    fn withdraw_fee(who: &H160, fee: U256) -> Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
97        pallet_evm::EVMFungibleAdapter::<F, FeeHandler>::withdraw_fee(who, fee)
98    }
99
100    fn can_withdraw(who: &H160, amount: U256) -> Result<(), pallet_evm::Error<T>> {
101        pallet_evm::EVMFungibleAdapter::<F, FeeHandler>::can_withdraw(who, amount)
102    }
103
104    fn correct_and_deposit_fee(
105        who: &H160,
106        corrected_fee: U256,
107        base_fee: U256,
108        already_withdrawn: Self::LiquidityInfo,
109    ) -> Self::LiquidityInfo {
110        <pallet_evm::EVMFungibleAdapter::<F, FeeHandler> as OnChargeEVMTransaction<T>>::correct_and_deposit_fee(
111            who,
112            corrected_fee,
113            base_fee,
114            already_withdrawn,
115        )
116    }
117
118    fn pay_priority_fee(tip: Self::LiquidityInfo) {
119        if let Some(tip) = tip {
120            TipHandler::on_unbalanceds(Some(tip).into_iter());
121        }
122    }
123}