example_erc20_gas/
handler.rs1use 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, FrameResult, FrameTr,
9 Handler,
10 },
11 interpreter::interpreter_action::FrameInit,
12 primitives::{hardfork::SpecId, U256},
13 state::EvmState,
14};
15
16use crate::{erc_address_storage, token_operation, TOKEN, TREASURY};
17
18#[derive(Debug)]
22pub struct Erc20MainnetHandler<EVM, ERROR, FRAME> {
23 _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>,
24}
25
26impl<CTX, ERROR, FRAME> Erc20MainnetHandler<CTX, ERROR, FRAME> {
27 pub fn new() -> Self {
29 Self {
30 _phantom: core::marker::PhantomData,
31 }
32 }
33}
34
35impl<EVM, ERROR, FRAME> Default for Erc20MainnetHandler<EVM, ERROR, FRAME> {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl<EVM, ERROR, FRAME> Handler for Erc20MainnetHandler<EVM, ERROR, FRAME>
42where
43 EVM: EvmTr<Context: ContextTr<Journal: JournalTr<State = EvmState>>, Frame = FRAME>,
44 FRAME: FrameTr<FrameResult = FrameResult, FrameInit = FrameInit>,
45 ERROR: EvmTrError<EVM>,
46{
47 type Evm = EVM;
48 type Error = ERROR;
49 type HaltReason = HaltReason;
50
51 fn validate_against_state_and_deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), ERROR> {
52 let context = evm.ctx();
53 let basefee = context.block().basefee() as u128;
54 let blob_price = context.block().blob_gasprice().unwrap_or_default();
55 let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
56 let is_eip3607_disabled = context.cfg().is_eip3607_disabled();
57 let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled();
58 let caller = context.tx().caller();
59 let value = context.tx().value();
60
61 let (tx, journal) = context.tx_journal_mut();
62
63 let caller_account = journal.load_account_code(tx.caller())?.data;
65
66 validate_account_nonce_and_code(
67 &mut caller_account.info,
68 tx.nonce(),
69 is_eip3607_disabled,
70 is_nonce_check_disabled,
71 )?;
72
73 if tx.kind().is_call() {
74 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
75 }
76
77 caller_account.mark_touch();
79
80 let max_balance_spending = tx.max_balance_spending()?;
81 let effective_balance_spending = tx
82 .effective_balance_spending(basefee, blob_price)
83 .expect("effective balance is always smaller than max balance so it can't overflow");
84
85 let account_balance_slot = erc_address_storage(tx.caller());
86 context.journal_mut().load_account(TOKEN)?.data.mark_touch();
87
88 let account_balance = context
89 .journal_mut()
90 .sload(TOKEN, account_balance_slot)
91 .map(|v| v.data)
92 .unwrap_or_default();
93
94 if account_balance < max_balance_spending && !is_balance_check_disabled {
95 return Err(InvalidTransaction::LackOfFundForMaxFee {
96 fee: Box::new(max_balance_spending),
97 balance: Box::new(account_balance),
98 }
99 .into());
100 };
101
102 if is_balance_check_disabled {
105 } else if max_balance_spending > account_balance {
107 return Err(InvalidTransaction::LackOfFundForMaxFee {
108 fee: Box::new(max_balance_spending),
109 balance: Box::new(account_balance),
110 }
111 .into());
112 } else {
113 let gas_balance_spending = effective_balance_spending - value;
115
116 token_operation::<EVM::Context, ERROR>(
117 context,
118 caller,
119 TREASURY,
120 gas_balance_spending,
121 )?;
122 }
123
124 Ok(())
125 }
126
127 fn reimburse_caller(
128 &self,
129 evm: &mut Self::Evm,
130 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
131 ) -> Result<(), Self::Error> {
132 let context = evm.ctx();
133 let basefee = context.block().basefee() as u128;
134 let caller = context.tx().caller();
135 let effective_gas_price = context.tx().effective_gas_price(basefee);
136 let gas = exec_result.gas();
137
138 let reimbursement =
139 effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128);
140
141 token_operation::<EVM::Context, ERROR>(
142 context,
143 TREASURY,
144 caller,
145 U256::from(reimbursement),
146 )?;
147
148 Ok(())
149 }
150
151 fn reward_beneficiary(
152 &self,
153 evm: &mut Self::Evm,
154 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
155 ) -> Result<(), Self::Error> {
156 let context = evm.ctx();
157 let tx = context.tx();
158 let beneficiary = context.block().beneficiary();
159 let basefee = context.block().basefee() as u128;
160 let effective_gas_price = tx.effective_gas_price(basefee);
161 let gas = exec_result.gas();
162
163 let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
164 effective_gas_price.saturating_sub(basefee)
165 } else {
166 effective_gas_price
167 };
168
169 let reward = coinbase_gas_price.saturating_mul(gas.used() as u128);
170 token_operation::<EVM::Context, ERROR>(context, TREASURY, beneficiary, U256::from(reward))?;
171
172 Ok(())
173 }
174}