revm_handler/
pre_execution.rs1use bytecode::Bytecode;
6use context_interface::transaction::{AccessListItemTr, AuthorizationTr};
7use context_interface::ContextTr;
8use context_interface::{
9 journaled_state::JournalTr,
10 result::InvalidTransaction,
11 transaction::{Transaction, TransactionType},
12 Block, Cfg, Database,
13};
14use primitives::{eip7702, hardfork::SpecId, KECCAK_EMPTY, U256};
15
16use crate::{EvmTr, PrecompileProvider};
17
18pub fn load_accounts<
19 EVM: EvmTr<Precompiles: PrecompileProvider<EVM::Context>>,
20 ERROR: From<<<EVM::Context as ContextTr>::Db as Database>::Error>,
21>(
22 evm: &mut EVM,
23) -> Result<(), ERROR> {
24 let (context, precompiles) = evm.ctx_precompiles();
25
26 let gen_spec = context.cfg().spec();
27 let spec = gen_spec.clone().into();
28 context.journal().set_spec_id(spec);
30 let precompiles_changed = precompiles.set_spec(gen_spec);
31 let empty_warmed_precompiles = context.journal().precompile_addresses().is_empty();
32
33 if precompiles_changed || empty_warmed_precompiles {
34 context
37 .journal()
38 .warm_precompiles(precompiles.warm_addresses().collect());
39 }
40
41 if spec.is_enabled_in(SpecId::SHANGHAI) {
44 let coinbase = context.block().beneficiary();
45 context.journal().warm_account(coinbase);
46 }
47
48 let (tx, journal) = context.tx_journal();
50 if tx.tx_type() != TransactionType::Legacy {
52 if let Some(access_list) = tx.access_list() {
53 for item in access_list {
54 let address = item.address();
55 let mut storage = item.storage_slots().peekable();
56 if storage.peek().is_none() {
57 journal.warm_account(*address);
58 } else {
59 journal.warm_account_and_storage(
60 *address,
61 storage.map(|i| U256::from_be_bytes(i.0)),
62 )?;
63 }
64 }
65 }
66 }
67
68 Ok(())
69}
70
71#[inline]
72pub fn deduct_caller<CTX: ContextTr>(
73 context: &mut CTX,
74) -> Result<(), <CTX::Db as Database>::Error> {
75 let basefee = context.block().basefee();
76 let blob_price = context.block().blob_gasprice().unwrap_or_default();
77 let effective_gas_price = context.tx().effective_gas_price(basefee as u128);
78 let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
79 let value = context.tx().value();
80
81 let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price);
84
85 if context.tx().tx_type() == TransactionType::Eip4844 {
87 let blob_gas = context.tx().total_blob_gas() as u128;
88 gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas));
89 }
90
91 let is_call = context.tx().kind().is_call();
92 let caller = context.tx().caller();
93
94 let caller_account = context.journal().load_account(caller)?.data;
96 caller_account.info.balance = caller_account
98 .info
99 .balance
100 .saturating_sub(U256::from(gas_cost));
101
102 if is_balance_check_disabled {
103 caller_account.info.balance = value.max(caller_account.info.balance);
105 }
106
107 if is_call {
109 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
111 }
112
113 caller_account.mark_touch();
115 Ok(())
116}
117
118#[inline]
120pub fn apply_eip7702_auth_list<
121 CTX: ContextTr,
122 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
123>(
124 context: &mut CTX,
125) -> Result<u64, ERROR> {
126 let spec = context.cfg().spec().into();
127 let tx = context.tx();
128 if !spec.is_enabled_in(SpecId::PRAGUE) {
129 return Ok(0);
130 }
131 if tx.tx_type() != TransactionType::Eip7702 {
133 return Ok(0);
134 }
135
136 let chain_id = context.cfg().chain_id();
137 let (tx, journal) = context.tx_journal();
138
139 let mut refunded_accounts = 0;
140 for authorization in tx.authorization_list() {
141 let auth_chain_id = authorization.chain_id();
143 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
144 continue;
145 }
146
147 if authorization.nonce() == u64::MAX {
149 continue;
150 }
151
152 let Some(authority) = authorization.authority() else {
155 continue;
156 };
157
158 let mut authority_acc = journal.load_account_code(authority)?;
161
162 if let Some(bytecode) = &authority_acc.info.code {
164 if !bytecode.is_empty() && !bytecode.is_eip7702() {
166 continue;
167 }
168 }
169
170 if authorization.nonce() != authority_acc.info.nonce {
172 continue;
173 }
174
175 if !authority_acc.is_empty() {
177 refunded_accounts += 1;
178 }
179
180 let address = authorization.address();
184 let (bytecode, hash) = if address.is_zero() {
185 (Bytecode::default(), KECCAK_EMPTY)
186 } else {
187 let bytecode = Bytecode::new_eip7702(address);
188 let hash = bytecode.hash_slow();
189 (bytecode, hash)
190 };
191 authority_acc.info.code_hash = hash;
192 authority_acc.info.code = Some(bytecode);
193
194 authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
196 authority_acc.mark_touch();
197 }
198
199 let refunded_gas =
200 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
201
202 Ok(refunded_gas)
203}