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 let account_balance = context
87 .journal_mut()
88 .sload(TOKEN, account_balance_slot)
89 .map(|v| v.data)
90 .unwrap_or_default();
91
92 if account_balance < max_balance_spending && !is_balance_check_disabled {
93 return Err(InvalidTransaction::LackOfFundForMaxFee {
94 fee: Box::new(max_balance_spending),
95 balance: Box::new(account_balance),
96 }
97 .into());
98 };
99
100 if is_balance_check_disabled {
103 } else if max_balance_spending > account_balance {
106 return Err(InvalidTransaction::LackOfFundForMaxFee {
107 fee: Box::new(max_balance_spending),
108 balance: Box::new(account_balance),
109 }
110 .into());
111 } else {
112 let gas_balance_spending = effective_balance_spending - value;
114
115 token_operation::<EVM::Context, ERROR>(
116 context,
117 caller,
118 TREASURY,
119 gas_balance_spending,
120 )?;
121 }
122
123 Ok(())
124 }
125
126 fn reimburse_caller(
127 &self,
128 evm: &mut Self::Evm,
129 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
130 ) -> Result<(), Self::Error> {
131 let context = evm.ctx();
132 let basefee = context.block().basefee() as u128;
133 let caller = context.tx().caller();
134 let effective_gas_price = context.tx().effective_gas_price(basefee);
135 let gas = exec_result.gas();
136
137 let reimbursement =
138 effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128);
139 token_operation::<EVM::Context, ERROR>(
140 context,
141 TREASURY,
142 caller,
143 U256::from(reimbursement),
144 )?;
145
146 Ok(())
147 }
148
149 fn reward_beneficiary(
150 &self,
151 evm: &mut Self::Evm,
152 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
153 ) -> Result<(), Self::Error> {
154 let context = evm.ctx();
155 let tx = context.tx();
156 let beneficiary = context.block().beneficiary();
157 let basefee = context.block().basefee() as u128;
158 let effective_gas_price = tx.effective_gas_price(basefee);
159 let gas = exec_result.gas();
160
161 let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
162 effective_gas_price.saturating_sub(basefee)
163 } else {
164 effective_gas_price
165 };
166
167 let reward =
168 coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128);
169 token_operation::<EVM::Context, ERROR>(context, TREASURY, beneficiary, U256::from(reward))?;
170
171 Ok(())
172 }
173}