revm/handler/mainnet/
pre_execution.rs
1use crate::{
6 precompile::PrecompileSpecId,
7 primitives::{
8 db::Database,
9 eip7702, Account, Bytecode, EVMError, Env, Spec,
10 SpecId::{CANCUN, PRAGUE, SHANGHAI},
11 TxKind, BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256,
12 },
13 Context, ContextPrecompiles,
14};
15
16#[inline]
18pub fn load_precompiles<SPEC: Spec, DB: Database>() -> ContextPrecompiles<DB> {
19 ContextPrecompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID))
20}
21
22#[inline]
24pub fn load_accounts<SPEC: Spec, EXT, DB: Database>(
25 context: &mut Context<EXT, DB>,
26) -> Result<(), EVMError<DB::Error>> {
27 context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID);
29
30 if SPEC::enabled(SHANGHAI) {
33 let coinbase = context.evm.inner.env.block.coinbase;
34 context
35 .evm
36 .journaled_state
37 .warm_preloaded_addresses
38 .insert(coinbase);
39 }
40
41 if SPEC::enabled(PRAGUE) {
44 context
45 .evm
46 .journaled_state
47 .warm_preloaded_addresses
48 .insert(BLOCKHASH_STORAGE_ADDRESS);
49 }
50
51 context.evm.load_access_list()?;
53 Ok(())
54}
55
56#[inline]
58pub fn deduct_caller_inner<SPEC: Spec>(caller_account: &mut Account, env: &Env) {
59 let mut gas_cost = U256::from(env.tx.gas_limit).saturating_mul(env.effective_gas_price());
62
63 if SPEC::enabled(CANCUN) {
65 let data_fee = env.calc_data_fee().expect("already checked");
66 gas_cost = gas_cost.saturating_add(data_fee);
67 }
68
69 caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost);
71
72 if matches!(env.tx.transact_to, TxKind::Call(_)) {
74 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
76 }
77
78 caller_account.mark_touch();
80}
81
82#[inline]
84pub fn deduct_caller<SPEC: Spec, EXT, DB: Database>(
85 context: &mut Context<EXT, DB>,
86) -> Result<(), EVMError<DB::Error>> {
87 let caller_account = context
89 .evm
90 .inner
91 .journaled_state
92 .load_account(context.evm.inner.env.tx.caller, &mut context.evm.inner.db)?;
93
94 deduct_caller_inner::<SPEC>(caller_account.data, &context.evm.inner.env);
96
97 Ok(())
98}
99
100#[inline]
102pub fn apply_eip7702_auth_list<SPEC: Spec, EXT, DB: Database>(
103 context: &mut Context<EXT, DB>,
104) -> Result<u64, EVMError<DB::Error>> {
105 if !SPEC::enabled(PRAGUE) {
107 return Ok(0);
108 }
109
110 let Some(authorization_list) = context.evm.inner.env.tx.authorization_list.as_ref() else {
112 return Ok(0);
113 };
114
115 let mut refunded_accounts = 0;
116 for authorization in authorization_list.recovered_iter() {
117 let chain_id = authorization.chain_id();
119 if chain_id != 0 && chain_id != context.evm.inner.env.cfg.chain_id {
120 continue;
121 }
122
123 if authorization.nonce() == u64::MAX {
125 continue;
126 }
127
128 let Some(authority) = authorization.authority() else {
131 continue;
132 };
133
134 let mut authority_acc = context
137 .evm
138 .inner
139 .journaled_state
140 .load_code(authority, &mut context.evm.inner.db)?;
141
142 if let Some(bytecode) = &authority_acc.info.code {
144 if !bytecode.is_empty() && !bytecode.is_eip7702() {
146 continue;
147 }
148 }
149
150 if authorization.nonce() != authority_acc.info.nonce {
152 continue;
153 }
154
155 if !authority_acc.is_empty() {
157 refunded_accounts += 1;
158 }
159
160 let (bytecode, hash) = if authorization.address.is_zero() {
163 (Bytecode::default(), KECCAK_EMPTY)
164 } else {
165 let bytecode = Bytecode::new_eip7702(authorization.address);
166 let hash = bytecode.hash_slow();
167 (bytecode, hash)
168 };
169 authority_acc.info.code_hash = hash;
170 authority_acc.info.code = Some(bytecode);
171
172 authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
174 authority_acc.mark_touch();
175 }
176
177 let refunded_gas =
178 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
179
180 Ok(refunded_gas)
181}