1use crate::{
2 constants::{POST_OSAKA_GAS_LIMIT_CAP, TX_MAX_GAS_LIMIT_AMSTERDAM},
3 db::gen_db::GeneralizedDatabase,
4 errors::{ContextResult, ExecutionReport, InternalError, TxValidationError, VMError},
5 hooks::{DefaultHook, default_hook, hook::Hook},
6 opcodes::Opcode,
7 tracing::LevmCallTracer,
8 vm::{VM, VMType},
9};
10
11use bytes::Bytes;
12use ethrex_common::{
13 Address, H160, H256, U256,
14 constants::GAS_PER_BLOB,
15 types::{
16 Code, EIP1559Transaction, Fork, Transaction, TxKind,
17 {
18 SAFE_BYTES_PER_BLOB,
19 fee_config::{FeeConfig, L1FeeConfig, OperatorFeeConfig},
20 },
21 },
22};
23use ethrex_rlp::encode::RLPEncode;
24
25pub const COMMON_BRIDGE_L2_ADDRESS: Address = H160([
26 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 0x00, 0x00, 0xff, 0xff,
28]);
29pub const FEE_TOKEN_REGISTRY_ADDRESS: Address = H160([
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31 0x00, 0x00, 0xff, 0xfc,
32]);
33pub const FEE_TOKEN_RATIO_ADDRESS: Address = H160([
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0x00, 0xff, 0xfb,
36]);
37
38const LOCK_FEE_SELECTOR: [u8; 4] = [0x89, 0x9c, 0x86, 0xe2];
40const PAY_FEE_SELECTOR: [u8; 4] = [0x72, 0x74, 0x6e, 0xaf];
42const IS_FEE_TOKEN_SELECTOR: [u8; 4] = [0x16, 0xad, 0x82, 0xd7];
44const FEE_TOKEN_RATIO_SELECTOR: [u8; 4] = [0xc6, 0xab, 0x85, 0xd8];
46const SIMULATION_GAS_LIMIT: u64 = 21000 * 100;
47const SIMULATION_MAX_FEE: u64 = 100;
48
49pub struct L2Hook {
50 pub fee_config: FeeConfig,
51 cached_fee_token_ratio: Option<U256>,
55}
56
57impl L2Hook {
58 pub fn new(fee_config: FeeConfig) -> Self {
59 Self {
60 fee_config,
61 cached_fee_token_ratio: None,
62 }
63 }
64}
65
66impl Hook for L2Hook {
67 fn prepare_execution(&mut self, vm: &mut VM<'_>) -> Result<(), crate::errors::VMError> {
68 if vm.env.is_privileged {
69 return prepare_execution_privileged(vm);
70 } else if vm.env.fee_token.is_some() {
71 let ratio = prepare_execution_fee_token(vm)?;
72 self.cached_fee_token_ratio = Some(ratio);
73 } else {
74 DefaultHook.prepare_execution(vm)?;
75 }
76 validate_sufficient_max_fee_per_gas_l2(vm, &self.fee_config.operator_fee_config)?;
79 reserve_l1_gas(vm, &self.fee_config.l1_fee_config)?;
82 Ok(())
83 }
84
85 fn finalize_execution(
86 &mut self,
87 vm: &mut VM<'_>,
88 ctx_result: &mut ContextResult,
89 ) -> Result<(), crate::errors::VMError> {
90 if vm.env.is_privileged {
91 if !ctx_result.is_success() && vm.env.origin != COMMON_BRIDGE_L2_ADDRESS {
92 default_hook::undo_value_transfer(vm)?;
93 }
94 default_hook::delete_self_destruct_accounts(vm)?;
97 } else {
98 finalize_non_privileged_execution(
99 vm,
100 ctx_result,
101 &self.fee_config,
102 vm.env.fee_token.is_some(),
103 self.cached_fee_token_ratio,
104 )?;
105 }
106
107 Ok(())
108 }
109}
110
111fn finalize_non_privileged_execution(
115 vm: &mut VM<'_>,
116 ctx_result: &mut ContextResult,
117 fee_config: &FeeConfig,
118 use_fee_token: bool,
119 cached_fee_token_ratio: Option<U256>,
120) -> Result<(), crate::errors::VMError> {
121 if !ctx_result.is_success() {
122 default_hook::undo_value_transfer(vm)?;
123 }
124
125 let l1_gas = calculate_l1_fee_gas(vm, &fee_config.l1_fee_config)?;
126
127 let execution_gas_pre_refund = ctx_result
130 .gas_used
131 .checked_sub(l1_gas)
132 .ok_or(InternalError::Underflow)?;
133
134 let gas_refunded: u64 = vm
136 .substate
137 .refunded_gas
138 .min(execution_gas_pre_refund / default_hook::MAX_REFUND_QUOTIENT);
139 let execution_gas =
140 default_hook::compute_actual_gas_used(vm, gas_refunded, execution_gas_pre_refund)?;
141
142 let actual_gas_used = execution_gas
143 .checked_add(l1_gas)
144 .ok_or(InternalError::Overflow)?;
145
146 let total_gas_pre_refund = ctx_result.gas_used;
148
149 let execution_backup = std::mem::take(&mut vm.current_call_frame.call_frame_backup);
152
153 let fee_token_ratio: u64 = match (cached_fee_token_ratio, use_fee_token) {
157 (Some(cached), _) => {
158 cached.try_into().map_err(|_| {
162 VMError::Internal(InternalError::Custom(
163 "Failed to convert fee token ratio".to_owned(),
164 ))
165 })?
166 }
167 (None, true) => {
168 return Err(VMError::Internal(InternalError::Custom(
169 "use_fee_token is true but fee_token_ratio was not cached".to_owned(),
170 )));
171 }
172 (None, false) => 1u64,
173 };
174
175 let result = apply_finalize_mutations(
181 vm,
182 ctx_result,
183 fee_config,
184 use_fee_token,
185 fee_token_ratio,
186 l1_gas,
187 gas_refunded,
188 execution_gas,
189 actual_gas_used,
190 total_gas_pre_refund,
191 );
192
193 if let Err(e) = result {
194 vm.restore_cache_state()?;
200 return Err(e);
201 }
202
203 vm.current_call_frame
206 .call_frame_backup
207 .extend(execution_backup);
208
209 Ok(())
210}
211
212#[allow(clippy::too_many_arguments)]
215fn apply_finalize_mutations(
216 vm: &mut VM<'_>,
217 ctx_result: &mut ContextResult,
218 fee_config: &FeeConfig,
219 use_fee_token: bool,
220 fee_token_ratio: u64,
221 l1_gas: u64,
222 gas_refunded: u64,
223 execution_gas: u64,
224 actual_gas_used: u64,
225 total_gas_pre_refund: u64,
226) -> Result<(), crate::errors::VMError> {
227 default_hook::delete_self_destruct_accounts(vm)?;
228
229 if let Some(l1_fee_config) = fee_config.l1_fee_config {
230 pay_to_l1_fee_vault(
231 vm,
232 l1_gas.saturating_mul(fee_token_ratio),
233 l1_fee_config,
234 use_fee_token,
235 )?;
236 }
237
238 if use_fee_token {
239 refund_sender_fee_token(
240 vm,
241 ctx_result,
242 gas_refunded,
243 actual_gas_used,
244 total_gas_pre_refund,
245 fee_token_ratio,
246 )?;
247 } else {
248 default_hook::refund_sender(vm, ctx_result, gas_refunded, actual_gas_used)?;
249 }
250
251 pay_coinbase_l2(
252 vm,
253 execution_gas.saturating_mul(fee_token_ratio),
254 &fee_config.operator_fee_config,
255 use_fee_token,
256 )?;
257
258 if let Some(base_fee_vault) = fee_config.base_fee_vault {
263 pay_base_fee_vault(
264 vm,
265 execution_gas.saturating_mul(fee_token_ratio),
266 base_fee_vault,
267 use_fee_token,
268 )?;
269 } else if use_fee_token {
270 pay_base_fee_vault(
271 vm,
272 execution_gas.saturating_mul(fee_token_ratio),
273 Address::zero(),
274 use_fee_token,
275 )?;
276 }
277
278 if let Some(operator_fee_config) = fee_config.operator_fee_config {
279 pay_operator_fee(
280 vm,
281 execution_gas.saturating_mul(fee_token_ratio),
282 operator_fee_config,
283 use_fee_token,
284 )?;
285 }
286
287 Ok(())
291}
292
293fn validate_sufficient_max_fee_per_gas_l2(
294 vm: &VM<'_>,
295 operator_fee_config: &Option<OperatorFeeConfig>,
296) -> Result<(), TxValidationError> {
297 let Some(fee_config) = operator_fee_config else {
298 return Ok(());
300 };
301
302 let total_fee = vm
303 .env
304 .base_fee_per_gas
305 .checked_add(fee_config.operator_fee_per_gas.into())
306 .ok_or(TxValidationError::InsufficientMaxFeePerGas)?;
307
308 if vm.env.tx_max_fee_per_gas.unwrap_or(vm.env.gas_price) < total_fee {
309 return Err(TxValidationError::InsufficientMaxFeePerGas);
310 }
311 Ok(())
312}
313
314fn reserve_l1_gas(vm: &mut VM<'_>, l1_fee_config: &Option<L1FeeConfig>) -> Result<(), VMError> {
336 let l1_gas = calculate_l1_fee_gas(vm, l1_fee_config)?;
337
338 if vm.env.config.fork >= Fork::Prague {
342 let floor = vm.get_min_gas_used()?;
343 let floor_plus_l1 = floor.checked_add(l1_gas).ok_or(InternalError::Overflow)?;
344 if vm.env.gas_limit < floor_plus_l1 {
345 return Err(TxValidationError::L1GasReservationTooLow.into());
346 }
347 }
348
349 vm.current_call_frame
350 .increase_consumed_gas(l1_gas)
351 .map_err(|_| TxValidationError::L1GasReservationTooLow)?;
352 Ok(())
353}
354
355fn pay_coinbase_l2(
359 vm: &mut VM<'_>,
360 gas_to_pay: u64,
361 operator_fee_config: &Option<OperatorFeeConfig>,
362 use_fee_token: bool,
363) -> Result<(), crate::errors::VMError> {
364 if operator_fee_config.is_none() && !use_fee_token {
365 return default_hook::pay_coinbase(vm, gas_to_pay);
367 }
368
369 let priority_fee_per_gas = compute_priority_fee_per_gas(vm, operator_fee_config)?;
370
371 let coinbase_fee = U256::from(gas_to_pay)
372 .checked_mul(priority_fee_per_gas)
373 .ok_or(InternalError::Overflow)?;
374
375 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
379 recorder.record_touched_address(vm.env.coinbase);
380 }
381
382 if !coinbase_fee.is_zero() {
383 if use_fee_token {
384 pay_fee_token(vm, vm.env.coinbase, coinbase_fee)?;
385 } else {
386 vm.increase_account_balance(vm.env.coinbase, coinbase_fee)?;
387 }
388 }
389
390 Ok(())
391}
392
393fn compute_priority_fee_per_gas(
396 vm: &VM<'_>,
397 operator_fee_config: &Option<OperatorFeeConfig>,
398) -> Result<U256, InternalError> {
399 let priority_fee = vm
400 .env
401 .gas_price
402 .checked_sub(vm.env.base_fee_per_gas)
403 .ok_or(InternalError::Underflow)?;
404
405 if let Some(fee_config) = operator_fee_config {
406 priority_fee
407 .checked_sub(U256::from(fee_config.operator_fee_per_gas))
408 .ok_or(InternalError::Underflow)
409 } else {
410 Ok(priority_fee)
411 }
412}
413
414fn pay_base_fee_vault(
418 vm: &mut VM<'_>,
419 gas_to_pay: u64,
420 base_fee_vault: Address,
421 use_fee_token: bool,
422) -> Result<(), crate::errors::VMError> {
423 let base_fee = U256::from(gas_to_pay)
424 .checked_mul(vm.env.base_fee_per_gas)
425 .ok_or(InternalError::Overflow)?;
426
427 if use_fee_token {
428 pay_fee_token(vm, base_fee_vault, base_fee)?;
429 } else {
430 vm.increase_account_balance(base_fee_vault, base_fee)?;
431 }
432 Ok(())
433}
434
435fn pay_operator_fee(
439 vm: &mut VM<'_>,
440 gas_to_pay: u64,
441 operator_fee_config: OperatorFeeConfig,
442 use_fee_token: bool,
443) -> Result<(), crate::errors::VMError> {
444 let operator_fee = U256::from(gas_to_pay)
445 .checked_mul(U256::from(operator_fee_config.operator_fee_per_gas))
446 .ok_or(InternalError::Overflow)?;
447
448 if use_fee_token {
449 pay_fee_token(vm, operator_fee_config.operator_fee_vault, operator_fee)?;
450 } else {
451 vm.increase_account_balance(operator_fee_config.operator_fee_vault, operator_fee)?;
452 }
453 Ok(())
454}
455
456fn prepare_execution_privileged(vm: &mut VM<'_>) -> Result<(), crate::errors::VMError> {
460 let sender_address = vm.env.origin;
461 let sender_balance = vm.db.get_account(sender_address)?.info.balance;
462
463 let mut tx_should_fail = false;
464
465 if sender_address != COMMON_BRIDGE_L2_ADDRESS {
471 let value = vm.current_call_frame.msg_value;
472 if value > sender_balance {
473 tx_should_fail = true;
474 }
475 }
480
481 let intrinsic_failed = match vm.get_intrinsic_gas() {
502 Ok(intrinsic) => vm.add_intrinsic_gas(&intrinsic).is_err(),
503 Err(_) => true,
504 };
505 if intrinsic_failed {
506 tx_should_fail = true;
507 }
508
509 default_hook::validate_gas_allowance(vm)?;
521
522 if tx_should_fail {
529 vm.current_call_frame.msg_value = U256::zero();
532 vm.current_call_frame
533 .set_code(Code::from_bytecode_unchecked(
534 vec![Opcode::INVALID.into()].into(),
535 H256::zero(),
536 ))?;
537 return Ok(());
538 }
539
540 if sender_address != COMMON_BRIDGE_L2_ADDRESS {
544 let value = vm.current_call_frame.msg_value;
545 vm.decrease_account_balance(sender_address, value)
546 .map_err(|_| {
547 InternalError::Custom("Insufficient funds in privileged transaction".to_string())
548 })?;
549 }
550
551 default_hook::transfer_value(vm)?;
552
553 default_hook::set_bytecode_and_code_address(vm)
554}
555
556fn prepare_execution_fee_token(vm: &mut VM<'_>) -> Result<U256, crate::errors::VMError> {
560 let fee_token = vm
561 .env
562 .fee_token
563 .ok_or(VMError::Internal(InternalError::Custom(
564 "Fee token address not provided".to_owned(),
565 )))?;
566
567 let (execution_result, _) = simulate_common_bridge_call(
568 vm,
569 FEE_TOKEN_REGISTRY_ADDRESS,
570 encode_is_fee_token_call(fee_token),
571 )?;
572
573 if !execution_result.is_success() {
574 return Err(VMError::TxValidation(
575 TxValidationError::InsufficientAccountFunds,
576 ));
577 }
578 if execution_result.output.len() != 32
582 || execution_result.output.get(31).is_none_or(|&b| b == 0)
583 {
584 return Err(VMError::TxValidation(
585 TxValidationError::InsufficientAccountFunds,
586 ));
587 }
588 let fee_token_ratio = get_fee_token_ratio(vm, fee_token)?;
589
590 let sender_address = vm.env.origin;
591 let sender_info = vm.db.get_account(sender_address)?.info.clone();
592
593 let intrinsic = vm.get_intrinsic_gas()?;
596
597 if vm.env.config.fork >= Fork::Prague {
598 default_hook::validate_min_gas_limit(vm, &intrinsic)?;
599 if !vm.env.disable_gas_allowance_check
602 && vm.env.config.fork < Fork::Amsterdam
603 && vm.tx.gas_limit() > TX_MAX_GAS_LIMIT_AMSTERDAM
604 {
605 return Err(VMError::TxValidation(
606 TxValidationError::TxMaxGasLimitExceeded {
607 tx_hash: vm.tx.hash(vm.crypto),
608 tx_gas_limit: vm.tx.gas_limit(),
609 },
610 ));
611 }
612 if !vm.env.disable_gas_allowance_check
613 && vm.env.config.fork >= Fork::Osaka
614 && vm.env.config.fork < Fork::Amsterdam
615 && vm.tx.gas_limit() > POST_OSAKA_GAS_LIMIT_CAP
616 {
617 return Err(VMError::TxValidation(
618 TxValidationError::TxMaxGasLimitExceeded {
619 tx_hash: vm.tx.hash(vm.crypto),
620 tx_gas_limit: vm.tx.gas_limit(),
621 },
622 ));
623 }
624 }
625
626 let gaslimit_price_product = vm
628 .env
629 .gas_price
630 .checked_mul(vm.env.gas_limit.into())
631 .ok_or(TxValidationError::GasLimitPriceProductOverflow)?;
632
633 deduct_caller_fee_token(vm, gaslimit_price_product.saturating_mul(fee_token_ratio))?;
638
639 default_hook::validate_sufficient_max_fee_per_gas(vm)?;
641
642 if vm.is_create()? {
644 default_hook::validate_init_code_size(vm)?;
645 }
646
647 vm.add_intrinsic_gas(&intrinsic)?;
649
650 vm.increment_account_nonce(sender_address)
652 .map_err(|_| TxValidationError::NonceIsMax)?;
653
654 if !vm.env.disable_nonce_check && sender_info.nonce != vm.env.tx_nonce {
656 return Err(TxValidationError::NonceMismatch {
657 expected: sender_info.nonce,
658 actual: vm.env.tx_nonce,
659 }
660 .into());
661 }
662
663 if let (Some(tx_max_priority_fee), Some(tx_max_fee_per_gas)) = (
665 vm.env.tx_max_priority_fee_per_gas,
666 vm.env.tx_max_fee_per_gas,
667 ) && tx_max_priority_fee > tx_max_fee_per_gas
668 {
669 return Err(TxValidationError::PriorityGreaterThanMaxFeePerGas {
670 priority_fee: tx_max_priority_fee,
671 max_fee_per_gas: tx_max_fee_per_gas,
672 }
673 .into());
674 }
675
676 if !vm.env.disable_sender_eoa_check {
678 let code = vm.db.get_code(sender_info.code_hash)?;
679 default_hook::validate_sender(sender_address, code.code())?;
680 }
681
682 default_hook::validate_gas_allowance(vm)?;
684
685 default_hook::transfer_value(vm)?;
692
693 default_hook::set_bytecode_and_code_address(vm)?;
694 Ok(fee_token_ratio)
695}
696
697pub fn deduct_caller_fee_token(
701 vm: &mut VM<'_>,
702 gas_limit_price_product: U256,
703) -> Result<(), VMError> {
704 let sender_address = vm.env.origin;
706 let value = vm.current_call_frame.msg_value;
707
708 vm.decrease_account_balance(sender_address, value)
710 .map_err(|_| TxValidationError::InsufficientAccountFunds)?;
711
712 lock_fee_token(vm, sender_address, gas_limit_price_product)?;
714
715 Ok(())
716}
717
718fn encode_fee_token_call(selector: [u8; 4], address: Address, amount: U256) -> Bytes {
721 let mut data = Vec::with_capacity(4 + 32 + 32);
722 data.extend_from_slice(&selector);
723 data.extend_from_slice(&[0u8; 12]);
724 data.extend_from_slice(&address.0);
725 data.extend_from_slice(&amount.to_big_endian());
726 data.into()
727}
728
729fn encode_is_fee_token_call(token: Address) -> Bytes {
730 let mut data = Vec::with_capacity(4 + 32);
731 data.extend_from_slice(&IS_FEE_TOKEN_SELECTOR);
732 data.extend_from_slice(&[0u8; 12]);
733 data.extend_from_slice(&token.0);
734 data.into()
735}
736
737fn encode_fee_token_ratio_call(token: Address) -> Bytes {
738 let mut data = Vec::with_capacity(4 + 32);
739 data.extend_from_slice(&FEE_TOKEN_RATIO_SELECTOR);
740 data.extend_from_slice(&[0u8; 12]);
741 data.extend_from_slice(&token.0);
742 data.into()
743}
744
745fn lock_fee_token(vm: &mut VM<'_>, payer: Address, amount: U256) -> Result<(), VMError> {
747 transfer_fee_token(vm, encode_fee_token_call(LOCK_FEE_SELECTOR, payer, amount))
748}
749
750fn pay_fee_token(vm: &mut VM<'_>, receiver: Address, amount: U256) -> Result<(), VMError> {
752 transfer_fee_token(
753 vm,
754 encode_fee_token_call(PAY_FEE_SELECTOR, receiver, amount),
755 )
756}
757
758fn transfer_fee_token(vm: &mut VM<'_>, data: Bytes) -> Result<(), VMError> {
769 let fee_token = vm
770 .env
771 .fee_token
772 .ok_or(VMError::Internal(InternalError::Custom(
773 "No fee token address provided, this is a bug".to_owned(),
774 )))?;
775
776 let (execution_result, mut db_clone) = simulate_common_bridge_call(vm, fee_token, data)?;
777
778 if !execution_result.is_success() {
779 return Err(VMError::TxValidation(
780 TxValidationError::InsufficientAccountFunds,
781 ));
782 }
783 let new_storage = db_clone.get_account(fee_token)?.storage.clone();
784 let current_storage = vm.db.get_account(fee_token)?.storage.clone();
785
786 for (key, new_value) in &new_storage {
788 let old_value = current_storage.get(key).copied().unwrap_or_default();
789 if old_value != *new_value {
790 vm.backup_storage_slot(fee_token, *key, old_value)?;
791 }
792 }
793 for (key, &old_value) in ¤t_storage {
795 if !new_storage.contains_key(key) {
796 vm.backup_storage_slot(fee_token, *key, old_value)?;
797 }
798 }
799
800 vm.db.get_account_mut(fee_token)?.storage = new_storage;
802
803 let initial_state_fee_token = db_clone
805 .initial_accounts_state
806 .get(&fee_token)
807 .cloned()
808 .ok_or(VMError::Internal(InternalError::Custom(
809 "No initial state found for fee token".to_owned(),
810 )))?;
811 vm.db
813 .initial_accounts_state
814 .insert(fee_token, initial_state_fee_token);
815
816 Ok(())
817}
818
819fn simulate_common_bridge_call(
822 vm: &VM<'_>,
823 to: Address,
824 data: Bytes,
825) -> Result<(ExecutionReport, GeneralizedDatabase), VMError> {
826 let mut db_clone = vm.db.clone(); let origin = COMMON_BRIDGE_L2_ADDRESS; let nonce = db_clone.get_account(origin)?.info.nonce;
829 let simulation_tx = EIP1559Transaction {
830 chain_id: u64::try_from(vm.env.chain_id).map_err(|_| {
832 VMError::Internal(InternalError::Custom("chain_id overflows u64".to_string()))
833 })?,
834 nonce,
835 max_priority_fee_per_gas: SIMULATION_MAX_FEE,
836 max_fee_per_gas: SIMULATION_MAX_FEE,
837 gas_limit: SIMULATION_GAS_LIMIT,
838 to: TxKind::Call(to),
839 value: U256::zero(),
840 data,
841 ..Default::default()
842 };
843 let tx = Transaction::EIP1559Transaction(simulation_tx);
844 let mut env_clone = vm.env.clone();
845 env_clone.base_fee_per_gas = U256::zero();
847 env_clone.block_excess_blob_gas = None;
848 env_clone.gas_price = U256::zero();
849 env_clone.origin = origin;
850 env_clone.fee_token = None;
851 env_clone.gas_limit = SIMULATION_GAS_LIMIT;
852
853 let mut new_vm = VM::new(
854 env_clone,
855 &mut db_clone,
856 &tx,
857 LevmCallTracer::disabled(),
858 VMType::L2(Default::default()),
859 vm.crypto,
860 None,
861 )?;
862 new_vm.hooks = vec![];
863 default_hook::set_bytecode_and_code_address(&mut new_vm)?;
864 let execution_result = new_vm.execute()?;
865
866 Ok((execution_result, db_clone))
867}
868
869fn refund_sender_fee_token(
873 vm: &mut VM<'_>,
874 ctx_result: &mut ContextResult,
875 refunded_gas: u64,
876 gas_spent: u64,
877 gas_used_pre_refund: u64,
878 fee_token_ratio: u64,
879) -> Result<(), VMError> {
880 vm.substate.refunded_gas = refunded_gas;
881
882 if vm.env.config.fork >= Fork::Amsterdam {
884 ctx_result.gas_used = gas_used_pre_refund;
886 ctx_result.gas_spent = gas_spent;
888 } else {
889 ctx_result.gas_used = gas_spent;
891 ctx_result.gas_spent = gas_spent;
892 }
893
894 let gas_to_return = vm
896 .env
897 .gas_limit
898 .checked_sub(gas_spent)
899 .ok_or(InternalError::Underflow)?;
900
901 let erc20_return_amount = vm
902 .env
903 .gas_price
904 .checked_mul(U256::from(gas_to_return))
905 .ok_or(InternalError::Overflow)?;
906 let sender_address = vm.env.origin;
907
908 pay_fee_token(
909 vm,
910 sender_address,
911 erc20_return_amount.saturating_mul(fee_token_ratio.into()),
912 )?;
913
914 Ok(())
915}
916
917fn calculate_l1_fee(
921 fee_config: &L1FeeConfig,
922 transaction_size: usize,
923) -> Result<U256, crate::errors::VMError> {
924 let l1_fee_per_blob: U256 = fee_config
925 .l1_fee_per_blob_gas
926 .checked_mul(GAS_PER_BLOB.into())
927 .ok_or(InternalError::Overflow)?
928 .into();
929
930 let l1_fee_per_blob_byte = l1_fee_per_blob
931 .checked_div(U256::from(SAFE_BYTES_PER_BLOB))
932 .ok_or(InternalError::DivisionByZero)?;
933
934 let l1_fee = l1_fee_per_blob_byte
935 .checked_mul(U256::from(transaction_size))
936 .ok_or(InternalError::Overflow)?;
937
938 Ok(l1_fee)
939}
940
941fn calculate_l1_fee_gas(
944 vm: &VM<'_>,
945 l1_fee_config: &Option<L1FeeConfig>,
946) -> Result<u64, crate::errors::VMError> {
947 let Some(fee_config) = l1_fee_config else {
948 return Ok(0);
950 };
951
952 let tx_size = vm.tx.length();
953
954 let l1_fee = calculate_l1_fee(fee_config, tx_size)?;
955 let mut l1_fee_gas = l1_fee
956 .checked_div(vm.env.gas_price)
957 .ok_or(InternalError::DivisionByZero)?;
958
959 if l1_fee_gas == U256::zero() && l1_fee > U256::zero() {
961 l1_fee_gas = U256::one();
962 }
963
964 Ok(l1_fee_gas.try_into().map_err(|_| InternalError::Overflow)?)
965}
966
967fn pay_to_l1_fee_vault(
970 vm: &mut VM<'_>,
971 gas_to_pay: u64,
972 l1_fee_config: L1FeeConfig,
973 use_fee_token: bool,
974) -> Result<(), crate::errors::VMError> {
975 let l1_fee = U256::from(gas_to_pay)
976 .checked_mul(vm.env.gas_price)
977 .ok_or(InternalError::Overflow)?;
978
979 if use_fee_token {
980 pay_fee_token(vm, l1_fee_config.l1_fee_vault, l1_fee)?;
981 } else {
982 vm.increase_account_balance(l1_fee_config.l1_fee_vault, l1_fee)
983 .map_err(|_| TxValidationError::InsufficientAccountFunds)?;
984 }
985 Ok(())
986}
987
988fn get_fee_token_ratio(vm: &mut VM<'_>, fee_token: H160) -> Result<U256, VMError> {
989 let fee_token_ratio = simulate_common_bridge_call(
990 vm,
991 FEE_TOKEN_RATIO_ADDRESS,
992 encode_fee_token_ratio_call(fee_token),
993 )?
994 .0;
995 if !fee_token_ratio.is_success() || fee_token_ratio.output.len() != 32 {
996 return Err(VMError::Internal(InternalError::Custom(
997 "Failed to get fee token ratio".to_owned(),
998 )));
999 }
1000 Ok(U256::from_big_endian(
1001 fee_token_ratio
1002 .output
1003 .get(0..32)
1004 .ok_or(InternalError::Custom(
1005 "Failed to parse fee token ratio".to_owned(),
1006 ))?,
1007 ))
1008}