example_erc20_gas/
handler.rs

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