example_erc20_gas/
handler.rs1use core::cmp::Ordering;
2use revm::{
3 context::Cfg,
4 context_interface::{
5 result::{HaltReason, InvalidTransaction},
6 Block, ContextTr, Journal, Transaction, TransactionType,
7 },
8 handler::{EvmTr, EvmTrError, Frame, FrameResult, Handler},
9 interpreter::FrameInput,
10 primitives::{Log, U256},
11 specification::hardfork::SpecId,
12 state::EvmState,
13};
14
15use crate::{erc_address_storage, token_operation, TOKEN, TREASURY};
16
17pub struct Erc20MainetHandler<EVM, ERROR, FRAME> {
18 _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>,
19}
20
21impl<CTX, ERROR, FRAME> Erc20MainetHandler<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 Erc20MainetHandler<EVM, ERROR, FRAME> {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl<EVM, ERROR, FRAME> Handler for Erc20MainetHandler<EVM, ERROR, FRAME>
36where
37 EVM: EvmTr<Context: ContextTr<Journal: Journal<FinalOutput = (EvmState, Vec<Log>)>>>,
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_tx_against_state(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
47 let context = evm.ctx();
48 let caller = context.tx().caller();
49 let caller_nonce = context.journal().load_account(caller)?.data.info.nonce;
50 let _ = context.journal().load_account(TOKEN)?.data.clone();
51
52 if !context.cfg().is_nonce_check_disabled() {
53 let tx_nonce = context.tx().nonce();
54 let state_nonce = caller_nonce;
55 match tx_nonce.cmp(&state_nonce) {
56 Ordering::Less => {
57 return Err(ERROR::from(InvalidTransaction::NonceTooLow {
58 tx: tx_nonce,
59 state: state_nonce,
60 }))
61 }
62 Ordering::Greater => {
63 return Err(ERROR::from(InvalidTransaction::NonceTooHigh {
64 tx: tx_nonce,
65 state: state_nonce,
66 }))
67 }
68 _ => (),
69 }
70 }
71
72 let mut balance_check = U256::from(context.tx().gas_limit())
73 .checked_mul(U256::from(context.tx().max_fee_per_gas()))
74 .and_then(|gas_cost| gas_cost.checked_add(context.tx().value()))
75 .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
76
77 if context.tx().tx_type() == TransactionType::Eip4844 {
78 let tx = context.tx();
79 let data_fee = tx.calc_max_data_fee();
80 balance_check = balance_check
81 .checked_add(data_fee)
82 .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
83 }
84
85 let account_balance_slot = erc_address_storage(caller);
86 let account_balance = context
87 .journal()
88 .sload(TOKEN, account_balance_slot)
89 .map(|v| v.data)
90 .unwrap_or_default();
91
92 if account_balance < balance_check && !context.cfg().is_balance_check_disabled() {
93 return Err(InvalidTransaction::LackOfFundForMaxFee {
94 fee: Box::new(balance_check),
95 balance: Box::new(account_balance),
96 }
97 .into());
98 };
99
100 Ok(())
101 }
102
103 fn deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
104 let context = evm.ctx();
105 let _ = context.journal().load_account(TOKEN)?.data;
107 context.journal().touch_account(TOKEN);
108
109 let basefee = context.block().basefee() as u128;
110 let blob_price = context.block().blob_gasprice().unwrap_or_default();
111 let effective_gas_price = context.tx().effective_gas_price(basefee);
112
113 let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price);
114
115 if context.tx().tx_type() == TransactionType::Eip4844 {
116 let blob_gas = context.tx().total_blob_gas() as u128;
117 gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas));
118 }
119
120 let caller = context.tx().caller();
121 println!("Deduct caller: {:?} for amount: {gas_cost:?}", caller);
122 token_operation::<EVM::Context, ERROR>(context, caller, TREASURY, U256::from(gas_cost))?;
123
124 Ok(())
125 }
126
127 fn reimburse_caller(
128 &self,
129 evm: &mut Self::Evm,
130 exec_result: &mut <Self::Frame as Frame>::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 token_operation::<EVM::Context, ERROR>(
141 context,
142 TREASURY,
143 caller,
144 U256::from(reimbursement),
145 )?;
146
147 Ok(())
148 }
149
150 fn reward_beneficiary(
151 &self,
152 evm: &mut Self::Evm,
153 exec_result: &mut <Self::Frame as Frame>::FrameResult,
154 ) -> Result<(), Self::Error> {
155 let context = evm.ctx();
156 let tx = context.tx();
157 let beneficiary = context.block().beneficiary();
158 let basefee = context.block().basefee() as u128;
159 let effective_gas_price = tx.effective_gas_price(basefee);
160 let gas = exec_result.gas();
161
162 let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
163 effective_gas_price.saturating_sub(basefee)
164 } else {
165 effective_gas_price
166 };
167
168 let reward =
169 coinbase_gas_price.saturating_mul((gas.spent() - gas.refunded() as u64) as u128);
170 token_operation::<EVM::Context, ERROR>(context, TREASURY, beneficiary, U256::from(reward))?;
171
172 Ok(())
173 }
174}