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> {
329 let l1_gas = calculate_l1_fee_gas(vm, l1_fee_config)?;
330
331 if vm.env.config.fork >= Fork::Prague {
335 let floor = vm.get_min_gas_used()?;
336 let floor_plus_l1 = floor.checked_add(l1_gas).ok_or(InternalError::Overflow)?;
337 if vm.env.gas_limit < floor_plus_l1 {
338 return Err(TxValidationError::IntrinsicGasTooLow.into());
339 }
340 }
341
342 vm.current_call_frame
343 .increase_consumed_gas(l1_gas)
344 .map_err(|_| TxValidationError::IntrinsicGasTooLow)?;
345 Ok(())
346}
347
348fn pay_coinbase_l2(
352 vm: &mut VM<'_>,
353 gas_to_pay: u64,
354 operator_fee_config: &Option<OperatorFeeConfig>,
355 use_fee_token: bool,
356) -> Result<(), crate::errors::VMError> {
357 if operator_fee_config.is_none() && !use_fee_token {
358 return default_hook::pay_coinbase(vm, gas_to_pay);
360 }
361
362 let priority_fee_per_gas = compute_priority_fee_per_gas(vm, operator_fee_config)?;
363
364 let coinbase_fee = U256::from(gas_to_pay)
365 .checked_mul(priority_fee_per_gas)
366 .ok_or(InternalError::Overflow)?;
367
368 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
372 recorder.record_touched_address(vm.env.coinbase);
373 }
374
375 if !coinbase_fee.is_zero() {
376 if use_fee_token {
377 pay_fee_token(vm, vm.env.coinbase, coinbase_fee)?;
378 } else {
379 vm.increase_account_balance(vm.env.coinbase, coinbase_fee)?;
380 }
381 }
382
383 Ok(())
384}
385
386fn compute_priority_fee_per_gas(
389 vm: &VM<'_>,
390 operator_fee_config: &Option<OperatorFeeConfig>,
391) -> Result<U256, InternalError> {
392 let priority_fee = vm
393 .env
394 .gas_price
395 .checked_sub(vm.env.base_fee_per_gas)
396 .ok_or(InternalError::Underflow)?;
397
398 if let Some(fee_config) = operator_fee_config {
399 priority_fee
400 .checked_sub(U256::from(fee_config.operator_fee_per_gas))
401 .ok_or(InternalError::Underflow)
402 } else {
403 Ok(priority_fee)
404 }
405}
406
407fn pay_base_fee_vault(
411 vm: &mut VM<'_>,
412 gas_to_pay: u64,
413 base_fee_vault: Address,
414 use_fee_token: bool,
415) -> Result<(), crate::errors::VMError> {
416 let base_fee = U256::from(gas_to_pay)
417 .checked_mul(vm.env.base_fee_per_gas)
418 .ok_or(InternalError::Overflow)?;
419
420 if use_fee_token {
421 pay_fee_token(vm, base_fee_vault, base_fee)?;
422 } else {
423 vm.increase_account_balance(base_fee_vault, base_fee)?;
424 }
425 Ok(())
426}
427
428fn pay_operator_fee(
432 vm: &mut VM<'_>,
433 gas_to_pay: u64,
434 operator_fee_config: OperatorFeeConfig,
435 use_fee_token: bool,
436) -> Result<(), crate::errors::VMError> {
437 let operator_fee = U256::from(gas_to_pay)
438 .checked_mul(U256::from(operator_fee_config.operator_fee_per_gas))
439 .ok_or(InternalError::Overflow)?;
440
441 if use_fee_token {
442 pay_fee_token(vm, operator_fee_config.operator_fee_vault, operator_fee)?;
443 } else {
444 vm.increase_account_balance(operator_fee_config.operator_fee_vault, operator_fee)?;
445 }
446 Ok(())
447}
448
449fn prepare_execution_privileged(vm: &mut VM<'_>) -> Result<(), crate::errors::VMError> {
453 let sender_address = vm.env.origin;
454 let sender_balance = vm.db.get_account(sender_address)?.info.balance;
455
456 let mut tx_should_fail = false;
457
458 if sender_address != COMMON_BRIDGE_L2_ADDRESS {
464 let value = vm.current_call_frame.msg_value;
465 if value > sender_balance {
466 tx_should_fail = true;
467 }
468 }
473
474 let intrinsic_failed = match vm.get_intrinsic_gas() {
495 Ok(intrinsic) => vm.add_intrinsic_gas(&intrinsic).is_err(),
496 Err(_) => true,
497 };
498 if intrinsic_failed {
499 tx_should_fail = true;
500 }
501
502 default_hook::validate_gas_allowance(vm)?;
514
515 if tx_should_fail {
522 vm.current_call_frame.msg_value = U256::zero();
525 vm.current_call_frame
526 .set_code(Code::from_bytecode_unchecked(
527 vec![Opcode::INVALID.into()].into(),
528 H256::zero(),
529 ))?;
530 return Ok(());
531 }
532
533 if sender_address != COMMON_BRIDGE_L2_ADDRESS {
537 let value = vm.current_call_frame.msg_value;
538 vm.decrease_account_balance(sender_address, value)
539 .map_err(|_| {
540 InternalError::Custom("Insufficient funds in privileged transaction".to_string())
541 })?;
542 }
543
544 default_hook::transfer_value(vm)?;
545
546 default_hook::set_bytecode_and_code_address(vm)
547}
548
549fn prepare_execution_fee_token(vm: &mut VM<'_>) -> Result<U256, crate::errors::VMError> {
553 let fee_token = vm
554 .env
555 .fee_token
556 .ok_or(VMError::Internal(InternalError::Custom(
557 "Fee token address not provided".to_owned(),
558 )))?;
559
560 let (execution_result, _) = simulate_common_bridge_call(
561 vm,
562 FEE_TOKEN_REGISTRY_ADDRESS,
563 encode_is_fee_token_call(fee_token),
564 )?;
565
566 if !execution_result.is_success() {
567 return Err(VMError::TxValidation(
568 TxValidationError::InsufficientAccountFunds,
569 ));
570 }
571 if execution_result.output.len() != 32
575 || execution_result.output.get(31).is_none_or(|&b| b == 0)
576 {
577 return Err(VMError::TxValidation(
578 TxValidationError::InsufficientAccountFunds,
579 ));
580 }
581 let fee_token_ratio = get_fee_token_ratio(vm, fee_token)?;
582
583 let sender_address = vm.env.origin;
584 let sender_info = vm.db.get_account(sender_address)?.info.clone();
585
586 let intrinsic = vm.get_intrinsic_gas()?;
589
590 if vm.env.config.fork >= Fork::Prague {
591 default_hook::validate_min_gas_limit(vm, &intrinsic)?;
592 if vm.env.config.fork < Fork::Amsterdam && vm.tx.gas_limit() > TX_MAX_GAS_LIMIT_AMSTERDAM {
595 return Err(VMError::TxValidation(
596 TxValidationError::TxMaxGasLimitExceeded {
597 tx_hash: vm.tx.hash(),
598 tx_gas_limit: vm.tx.gas_limit(),
599 },
600 ));
601 }
602 if vm.env.config.fork >= Fork::Osaka
603 && vm.env.config.fork < Fork::Amsterdam
604 && vm.tx.gas_limit() > POST_OSAKA_GAS_LIMIT_CAP
605 {
606 return Err(VMError::TxValidation(
607 TxValidationError::TxMaxGasLimitExceeded {
608 tx_hash: vm.tx.hash(),
609 tx_gas_limit: vm.tx.gas_limit(),
610 },
611 ));
612 }
613 }
614
615 let gaslimit_price_product = vm
617 .env
618 .gas_price
619 .checked_mul(vm.env.gas_limit.into())
620 .ok_or(TxValidationError::GasLimitPriceProductOverflow)?;
621
622 deduct_caller_fee_token(vm, gaslimit_price_product.saturating_mul(fee_token_ratio))?;
627
628 default_hook::validate_sufficient_max_fee_per_gas(vm)?;
630
631 if vm.is_create()? {
633 default_hook::validate_init_code_size(vm)?;
634 }
635
636 vm.add_intrinsic_gas(&intrinsic)?;
638
639 vm.increment_account_nonce(sender_address)
641 .map_err(|_| TxValidationError::NonceIsMax)?;
642
643 if sender_info.nonce != vm.env.tx_nonce {
645 return Err(TxValidationError::NonceMismatch {
646 expected: sender_info.nonce,
647 actual: vm.env.tx_nonce,
648 }
649 .into());
650 }
651
652 if let (Some(tx_max_priority_fee), Some(tx_max_fee_per_gas)) = (
654 vm.env.tx_max_priority_fee_per_gas,
655 vm.env.tx_max_fee_per_gas,
656 ) && tx_max_priority_fee > tx_max_fee_per_gas
657 {
658 return Err(TxValidationError::PriorityGreaterThanMaxFeePerGas {
659 priority_fee: tx_max_priority_fee,
660 max_fee_per_gas: tx_max_fee_per_gas,
661 }
662 .into());
663 }
664
665 let code = vm.db.get_code(sender_info.code_hash)?;
667 default_hook::validate_sender(sender_address, code.code())?;
668
669 default_hook::validate_gas_allowance(vm)?;
671
672 default_hook::transfer_value(vm)?;
679
680 default_hook::set_bytecode_and_code_address(vm)?;
681 Ok(fee_token_ratio)
682}
683
684pub fn deduct_caller_fee_token(
688 vm: &mut VM<'_>,
689 gas_limit_price_product: U256,
690) -> Result<(), VMError> {
691 let sender_address = vm.env.origin;
693 let value = vm.current_call_frame.msg_value;
694
695 vm.decrease_account_balance(sender_address, value)
697 .map_err(|_| TxValidationError::InsufficientAccountFunds)?;
698
699 lock_fee_token(vm, sender_address, gas_limit_price_product)?;
701
702 Ok(())
703}
704
705fn encode_fee_token_call(selector: [u8; 4], address: Address, amount: U256) -> Bytes {
708 let mut data = Vec::with_capacity(4 + 32 + 32);
709 data.extend_from_slice(&selector);
710 data.extend_from_slice(&[0u8; 12]);
711 data.extend_from_slice(&address.0);
712 data.extend_from_slice(&amount.to_big_endian());
713 data.into()
714}
715
716fn encode_is_fee_token_call(token: Address) -> Bytes {
717 let mut data = Vec::with_capacity(4 + 32);
718 data.extend_from_slice(&IS_FEE_TOKEN_SELECTOR);
719 data.extend_from_slice(&[0u8; 12]);
720 data.extend_from_slice(&token.0);
721 data.into()
722}
723
724fn encode_fee_token_ratio_call(token: Address) -> Bytes {
725 let mut data = Vec::with_capacity(4 + 32);
726 data.extend_from_slice(&FEE_TOKEN_RATIO_SELECTOR);
727 data.extend_from_slice(&[0u8; 12]);
728 data.extend_from_slice(&token.0);
729 data.into()
730}
731
732fn lock_fee_token(vm: &mut VM<'_>, payer: Address, amount: U256) -> Result<(), VMError> {
734 transfer_fee_token(vm, encode_fee_token_call(LOCK_FEE_SELECTOR, payer, amount))
735}
736
737fn pay_fee_token(vm: &mut VM<'_>, receiver: Address, amount: U256) -> Result<(), VMError> {
739 transfer_fee_token(
740 vm,
741 encode_fee_token_call(PAY_FEE_SELECTOR, receiver, amount),
742 )
743}
744
745fn transfer_fee_token(vm: &mut VM<'_>, data: Bytes) -> Result<(), VMError> {
756 let fee_token = vm
757 .env
758 .fee_token
759 .ok_or(VMError::Internal(InternalError::Custom(
760 "No fee token address provided, this is a bug".to_owned(),
761 )))?;
762
763 let (execution_result, mut db_clone) = simulate_common_bridge_call(vm, fee_token, data)?;
764
765 if !execution_result.is_success() {
766 return Err(VMError::TxValidation(
767 TxValidationError::InsufficientAccountFunds,
768 ));
769 }
770 let new_storage = db_clone.get_account(fee_token)?.storage.clone();
771 let current_storage = vm.db.get_account(fee_token)?.storage.clone();
772
773 for (key, new_value) in &new_storage {
775 let old_value = current_storage.get(key).copied().unwrap_or_default();
776 if old_value != *new_value {
777 vm.backup_storage_slot(fee_token, *key, old_value)?;
778 }
779 }
780 for (key, &old_value) in ¤t_storage {
782 if !new_storage.contains_key(key) {
783 vm.backup_storage_slot(fee_token, *key, old_value)?;
784 }
785 }
786
787 vm.db.get_account_mut(fee_token)?.storage = new_storage;
789
790 let initial_state_fee_token = db_clone
792 .initial_accounts_state
793 .get(&fee_token)
794 .cloned()
795 .ok_or(VMError::Internal(InternalError::Custom(
796 "No initial state found for fee token".to_owned(),
797 )))?;
798 vm.db
800 .initial_accounts_state
801 .insert(fee_token, initial_state_fee_token);
802
803 Ok(())
804}
805
806fn simulate_common_bridge_call(
809 vm: &VM<'_>,
810 to: Address,
811 data: Bytes,
812) -> Result<(ExecutionReport, GeneralizedDatabase), VMError> {
813 let mut db_clone = vm.db.clone(); let origin = COMMON_BRIDGE_L2_ADDRESS; let nonce = db_clone.get_account(origin)?.info.nonce;
816 let simulation_tx = EIP1559Transaction {
817 chain_id: u64::try_from(vm.env.chain_id).map_err(|_| {
819 VMError::Internal(InternalError::Custom("chain_id overflows u64".to_string()))
820 })?,
821 nonce,
822 max_priority_fee_per_gas: SIMULATION_MAX_FEE,
823 max_fee_per_gas: SIMULATION_MAX_FEE,
824 gas_limit: SIMULATION_GAS_LIMIT,
825 to: TxKind::Call(to),
826 value: U256::zero(),
827 data,
828 ..Default::default()
829 };
830 let tx = Transaction::EIP1559Transaction(simulation_tx);
831 let mut env_clone = vm.env.clone();
832 env_clone.base_fee_per_gas = U256::zero();
834 env_clone.block_excess_blob_gas = None;
835 env_clone.gas_price = U256::zero();
836 env_clone.origin = origin;
837 env_clone.fee_token = None;
838 env_clone.gas_limit = SIMULATION_GAS_LIMIT;
839
840 let mut new_vm = VM::new(
841 env_clone,
842 &mut db_clone,
843 &tx,
844 LevmCallTracer::disabled(),
845 VMType::L2(Default::default()),
846 vm.crypto,
847 )?;
848 new_vm.hooks = vec![];
849 default_hook::set_bytecode_and_code_address(&mut new_vm)?;
850 let execution_result = new_vm.execute()?;
851
852 Ok((execution_result, db_clone))
853}
854
855fn refund_sender_fee_token(
859 vm: &mut VM<'_>,
860 ctx_result: &mut ContextResult,
861 refunded_gas: u64,
862 gas_spent: u64,
863 gas_used_pre_refund: u64,
864 fee_token_ratio: u64,
865) -> Result<(), VMError> {
866 vm.substate.refunded_gas = refunded_gas;
867
868 if vm.env.config.fork >= Fork::Amsterdam {
870 ctx_result.gas_used = gas_used_pre_refund;
872 ctx_result.gas_spent = gas_spent;
874 } else {
875 ctx_result.gas_used = gas_spent;
877 ctx_result.gas_spent = gas_spent;
878 }
879
880 let gas_to_return = vm
882 .env
883 .gas_limit
884 .checked_sub(gas_spent)
885 .ok_or(InternalError::Underflow)?;
886
887 let erc20_return_amount = vm
888 .env
889 .gas_price
890 .checked_mul(U256::from(gas_to_return))
891 .ok_or(InternalError::Overflow)?;
892 let sender_address = vm.env.origin;
893
894 pay_fee_token(
895 vm,
896 sender_address,
897 erc20_return_amount.saturating_mul(fee_token_ratio.into()),
898 )?;
899
900 Ok(())
901}
902
903fn calculate_l1_fee(
907 fee_config: &L1FeeConfig,
908 transaction_size: usize,
909) -> Result<U256, crate::errors::VMError> {
910 let l1_fee_per_blob: U256 = fee_config
911 .l1_fee_per_blob_gas
912 .checked_mul(GAS_PER_BLOB.into())
913 .ok_or(InternalError::Overflow)?
914 .into();
915
916 let l1_fee_per_blob_byte = l1_fee_per_blob
917 .checked_div(U256::from(SAFE_BYTES_PER_BLOB))
918 .ok_or(InternalError::DivisionByZero)?;
919
920 let l1_fee = l1_fee_per_blob_byte
921 .checked_mul(U256::from(transaction_size))
922 .ok_or(InternalError::Overflow)?;
923
924 Ok(l1_fee)
925}
926
927fn calculate_l1_fee_gas(
930 vm: &VM<'_>,
931 l1_fee_config: &Option<L1FeeConfig>,
932) -> Result<u64, crate::errors::VMError> {
933 let Some(fee_config) = l1_fee_config else {
934 return Ok(0);
936 };
937
938 let tx_size = vm.tx.length();
939
940 let l1_fee = calculate_l1_fee(fee_config, tx_size)?;
941 let mut l1_fee_gas = l1_fee
942 .checked_div(vm.env.gas_price)
943 .ok_or(InternalError::DivisionByZero)?;
944
945 if l1_fee_gas == U256::zero() && l1_fee > U256::zero() {
947 l1_fee_gas = U256::one();
948 }
949
950 Ok(l1_fee_gas.try_into().map_err(|_| InternalError::Overflow)?)
951}
952
953fn pay_to_l1_fee_vault(
956 vm: &mut VM<'_>,
957 gas_to_pay: u64,
958 l1_fee_config: L1FeeConfig,
959 use_fee_token: bool,
960) -> Result<(), crate::errors::VMError> {
961 let l1_fee = U256::from(gas_to_pay)
962 .checked_mul(vm.env.gas_price)
963 .ok_or(InternalError::Overflow)?;
964
965 if use_fee_token {
966 pay_fee_token(vm, l1_fee_config.l1_fee_vault, l1_fee)?;
967 } else {
968 vm.increase_account_balance(l1_fee_config.l1_fee_vault, l1_fee)
969 .map_err(|_| TxValidationError::InsufficientAccountFunds)?;
970 }
971 Ok(())
972}
973
974fn get_fee_token_ratio(vm: &mut VM<'_>, fee_token: H160) -> Result<U256, VMError> {
975 let fee_token_ratio = simulate_common_bridge_call(
976 vm,
977 FEE_TOKEN_RATIO_ADDRESS,
978 encode_fee_token_ratio_call(fee_token),
979 )?
980 .0;
981 if !fee_token_ratio.is_success() || fee_token_ratio.output.len() != 32 {
982 return Err(VMError::Internal(InternalError::Custom(
983 "Failed to get fee token ratio".to_owned(),
984 )));
985 }
986 Ok(U256::from_big_endian(
987 fee_token_ratio
988 .output
989 .get(0..32)
990 .ok_or(InternalError::Custom(
991 "Failed to parse fee token ratio".to_owned(),
992 ))?,
993 ))
994}