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 let Some(access_list) = tx.access_list() {
51 for item in access_list {
52 let address = item.address();
53 let mut storage = item.storage_slots().peekable();
54 if storage.peek().is_none() {
55 journal.warm_account(*address);
56 } else {
57 journal.warm_account_and_storage(
58 *address,
59 storage.map(|i| U256::from_be_bytes(i.0)),
60 )?;
61 }
62 }
63 }
64
65 Ok(())
66}
67
68#[inline]
69pub fn deduct_caller<CTX: ContextTr>(
70 context: &mut CTX,
71) -> Result<(), <CTX::Db as Database>::Error> {
72 let basefee = context.block().basefee();
73 let blob_price = context.block().blob_gasprice().unwrap_or_default();
74 let effective_gas_price = context.tx().effective_gas_price(basefee as u128);
75 let is_balance_check_disabled = context.cfg().is_balance_check_disabled();
76 let value = context.tx().value();
77
78 let mut gas_cost = (context.tx().gas_limit() as u128).saturating_mul(effective_gas_price);
81
82 if context.tx().tx_type() == TransactionType::Eip4844 {
84 let blob_gas = context.tx().total_blob_gas() as u128;
85 gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas));
86 }
87
88 let is_call = context.tx().kind().is_call();
89 let caller = context.tx().caller();
90
91 let caller_account = context.journal().load_account(caller)?.data;
93 caller_account.info.balance = caller_account
95 .info
96 .balance
97 .saturating_sub(U256::from(gas_cost));
98
99 if is_balance_check_disabled {
100 caller_account.info.balance = value.max(caller_account.info.balance);
102 }
103
104 if is_call {
106 caller_account.info.nonce = caller_account.info.nonce.saturating_add(1);
108 }
109
110 caller_account.mark_touch();
112 Ok(())
113}
114
115#[inline]
117pub fn apply_eip7702_auth_list<
118 CTX: ContextTr,
119 ERROR: From<InvalidTransaction> + From<<CTX::Db as Database>::Error>,
120>(
121 context: &mut CTX,
122) -> Result<u64, ERROR> {
123 let spec = context.cfg().spec().into();
124 let tx = context.tx();
125 if !spec.is_enabled_in(SpecId::PRAGUE) {
126 return Ok(0);
127 }
128 if tx.tx_type() != TransactionType::Eip7702 {
130 return Ok(0);
131 }
132
133 let chain_id = context.cfg().chain_id();
134 let (tx, journal) = context.tx_journal();
135
136 let mut refunded_accounts = 0;
137 for authorization in tx.authorization_list() {
138 let auth_chain_id = authorization.chain_id();
140 if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) {
141 continue;
142 }
143
144 if authorization.nonce() == u64::MAX {
146 continue;
147 }
148
149 let Some(authority) = authorization.authority() else {
152 continue;
153 };
154
155 let mut authority_acc = journal.load_account_code(authority)?;
158
159 if let Some(bytecode) = &authority_acc.info.code {
161 if !bytecode.is_empty() && !bytecode.is_eip7702() {
163 continue;
164 }
165 }
166
167 if authorization.nonce() != authority_acc.info.nonce {
169 continue;
170 }
171
172 if !authority_acc.is_empty() {
174 refunded_accounts += 1;
175 }
176
177 let address = authorization.address();
181 let (bytecode, hash) = if address.is_zero() {
182 (Bytecode::default(), KECCAK_EMPTY)
183 } else {
184 let bytecode = Bytecode::new_eip7702(address);
185 let hash = bytecode.hash_slow();
186 (bytecode, hash)
187 };
188 authority_acc.info.code_hash = hash;
189 authority_acc.info.code = Some(bytecode);
190
191 authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1);
193 authority_acc.mark_touch();
194 }
195
196 let refunded_gas =
197 refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST);
198
199 Ok(refunded_gas)
200}