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