example_erc20_gas/
handler.rs1use 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 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 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 if is_balance_check_disabled {
95 } 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 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}