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, 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 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 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 if is_balance_check_disabled {
99 } 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 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}