example_erc20_gas/
handler.rs

1use revm::{
2    context::Cfg,
3    context_interface::{
4        result::{HaltReason, InvalidTransaction},
5        Block, ContextTr, JournalTr, Transaction,
6    },
7    handler::{
8        pre_execution::validate_account_nonce_and_code, EvmTr, EvmTrError, Frame, FrameResult,
9        Handler,
10    },
11    interpreter::FrameInput,
12    primitives::{hardfork::SpecId, U256},
13    state::EvmState,
14};
15
16use crate::{erc_address_storage, token_operation, TOKEN, TREASURY};
17
18pub struct Erc20MainnetHandler<EVM, ERROR, FRAME> {
19    _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>,
20}
21
22impl<CTX, ERROR, FRAME> Erc20MainnetHandler<CTX, ERROR, FRAME> {
23    pub fn new() -> Self {
24        Self {
25            _phantom: core::marker::PhantomData,
26        }
27    }
28}
29
30impl<EVM, ERROR, FRAME> Default for Erc20MainnetHandler<EVM, ERROR, FRAME> {
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl<EVM, ERROR, FRAME> Handler for Erc20MainnetHandler<EVM, ERROR, FRAME>
37where
38    EVM: EvmTr<Context: ContextTr<Journal: JournalTr<State = EvmState>>>,
39    FRAME: Frame<Evm = EVM, Error = ERROR, FrameResult = FrameResult, FrameInit = FrameInput>,
40    ERROR: EvmTrError<EVM>,
41{
42    type Evm = EVM;
43    type Error = ERROR;
44    type Frame = FRAME;
45    type HaltReason = HaltReason;
46
47    fn validate_against_state_and_deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), ERROR> {
48        let context = evm.ctx();
49        let basefee = context.block().basefee() as u128;
50        let blob_price = context.block().blob_gasprice().unwrap_or_default();
51        let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
52        let is_eip3607_disabled = context.cfg().is_eip3607_disabled();
53        let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled();
54        let caller = context.tx().caller();
55        let value = context.tx().value();
56
57        let (tx, journal) = context.tx_journal_mut();
58
59        // Load caller's account.
60        let caller_account = journal.load_account_code(tx.caller())?.data;
61
62        validate_account_nonce_and_code(
63            &mut caller_account.info,
64            tx.nonce(),
65            is_eip3607_disabled,
66            is_nonce_check_disabled,
67        )?;
68
69        if tx.kind().is_call() {
70            caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
71        }
72
73        // Touch account so we know it is changed.
74        caller_account.mark_touch();
75
76        let max_balance_spending = tx.max_balance_spending()?;
77        let effective_balance_spending = tx
78            .effective_balance_spending(basefee, blob_price)
79            .expect("effective balance is always smaller than max balance so it can't overflow");
80
81        let account_balance_slot = erc_address_storage(tx.caller());
82        let account_balance = context
83            .journal_mut()
84            .sload(TOKEN, account_balance_slot)
85            .map(|v| v.data)
86            .unwrap_or_default();
87
88        if account_balance < max_balance_spending && !is_balance_check_disabled {
89            return Err(InvalidTransaction::LackOfFundForMaxFee {
90                fee: Box::new(max_balance_spending),
91                balance: Box::new(account_balance),
92            }
93            .into());
94        };
95
96        // Check if account has enough balance for `gas_limit * max_fee`` and value transfer.
97        // Transfer will be done inside `*_inner` functions.
98        if is_balance_check_disabled {
99            // ignore balance check.
100            // TODO add transfer value to the erc20 slot.
101        } else if max_balance_spending > account_balance {
102            return Err(InvalidTransaction::LackOfFundForMaxFee {
103                fee: Box::new(max_balance_spending),
104                balance: Box::new(account_balance),
105            }
106            .into());
107        } else {
108            // subtracting max balance spending with value that is going to be deducted later in the call.
109            let gas_balance_spending = effective_balance_spending - value;
110
111            token_operation::<EVM::Context, ERROR>(
112                context,
113                caller,
114                TREASURY,
115                gas_balance_spending,
116            )?;
117        }
118
119        Ok(())
120    }
121
122    fn reimburse_caller(
123        &self,
124        evm: &mut Self::Evm,
125        exec_result: &mut <Self::Frame as Frame>::FrameResult,
126    ) -> Result<(), Self::Error> {
127        let context = evm.ctx();
128        let basefee = context.block().basefee() as u128;
129        let caller = context.tx().caller();
130        let effective_gas_price = context.tx().effective_gas_price(basefee);
131        let gas = exec_result.gas();
132
133        let reimbursement =
134            effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128);
135        token_operation::<EVM::Context, ERROR>(
136            context,
137            TREASURY,
138            caller,
139            U256::from(reimbursement),
140        )?;
141
142        Ok(())
143    }
144
145    fn reward_beneficiary(
146        &self,
147        evm: &mut Self::Evm,
148        exec_result: &mut <Self::Frame as Frame>::FrameResult,
149    ) -> Result<(), Self::Error> {
150        let context = evm.ctx();
151        let tx = context.tx();
152        let beneficiary = context.block().beneficiary();
153        let basefee = context.block().basefee() as u128;
154        let effective_gas_price = tx.effective_gas_price(basefee);
155        let gas = exec_result.gas();
156
157        let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
158            effective_gas_price.saturating_sub(basefee)
159        } else {
160            effective_gas_price
161        };
162
163        let reward =
164            coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128);
165        token_operation::<EVM::Context, ERROR>(context, TREASURY, beneficiary, U256::from(reward))?;
166
167        Ok(())
168    }
169}