1pub mod precompiles;
4
5use crate::{
6 constants::{BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, OPERATOR_FEE_RECIPIENT},
7 transaction::{
8 deposit::{DepositTransaction, DEPOSIT_TRANSACTION_TYPE},
9 OpTransactionError, OpTxTr,
10 },
11 L1BlockInfo, OpHaltReason, OpSpecId,
12};
13use inspector::{Inspector, InspectorEvmTr, InspectorFrame, InspectorHandler};
14use precompile::Log;
15use revm::{
16 context_interface::{
17 result::{EVMError, ExecutionResult, FromStringError, ResultAndState},
18 Block, Cfg, ContextTr, Journal, Transaction,
19 },
20 handler::{
21 handler::{EvmTr, EvmTrError},
22 validation::validate_tx_against_account,
23 Frame, FrameResult, Handler, MainnetHandler,
24 },
25 interpreter::{interpreter::EthInterpreter, FrameInput, Gas},
26 primitives::{hash_map::HashMap, U256},
27 specification::hardfork::SpecId,
28 state::{Account, EvmState},
29 Database,
30};
31use std::vec::Vec;
32
33pub struct OpHandler<EVM, ERROR, FRAME> {
34 pub mainnet: MainnetHandler<EVM, ERROR, FRAME>,
35 pub _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>,
36}
37
38impl<EVM, ERROR, FRAME> OpHandler<EVM, ERROR, FRAME> {
39 pub fn new() -> Self {
40 Self {
41 mainnet: MainnetHandler::default(),
42 _phantom: core::marker::PhantomData,
43 }
44 }
45}
46
47impl<EVM, ERROR, FRAME> Default for OpHandler<EVM, ERROR, FRAME> {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53pub trait IsTxError {
54 fn is_tx_error(&self) -> bool;
55}
56
57impl<DB, TX> IsTxError for EVMError<DB, TX> {
58 fn is_tx_error(&self) -> bool {
59 matches!(self, EVMError::Transaction(_))
60 }
61}
62
63impl<EVM, ERROR, FRAME> Handler for OpHandler<EVM, ERROR, FRAME>
64where
65 EVM: EvmTr<
66 Context: ContextTr<
67 Journal: Journal<FinalOutput = (EvmState, Vec<Log>)>,
68 Tx: OpTxTr,
69 Cfg: Cfg<Spec = OpSpecId>,
70 Chain = L1BlockInfo,
71 >,
72 >,
73 ERROR: EvmTrError<EVM> + From<OpTransactionError> + FromStringError + IsTxError,
74 FRAME: Frame<Evm = EVM, Error = ERROR, FrameResult = FrameResult, FrameInit = FrameInput>,
77{
78 type Evm = EVM;
79 type Error = ERROR;
80 type Frame = FRAME;
81 type HaltReason = OpHaltReason;
82
83 fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
84 let ctx = evm.ctx();
86 let tx = ctx.tx();
87 let tx_type = tx.tx_type();
88 if tx_type == DEPOSIT_TRANSACTION_TYPE {
89 if tx.is_system_transaction()
91 && evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH)
92 {
93 return Err(OpTransactionError::DepositSystemTxPostRegolith.into());
94 }
95 return Ok(());
96 }
97 self.mainnet.validate_env(evm)
98 }
99
100 fn validate_tx_against_state(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
101 let context = evm.ctx();
102 if context.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE {
103 return Ok(());
104 }
105 let spec = context.cfg().spec();
106 let enveloped_tx = context
107 .tx()
108 .enveloped_tx()
109 .expect("all not deposit tx have enveloped tx")
110 .clone();
111 let mut additional_cost = context.chain().calculate_tx_l1_cost(&enveloped_tx, spec);
113
114 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
115 let gas_limit = U256::from(context.tx().gas_limit());
116 let operator_fee_charge = context
117 .chain()
118 .operator_fee_charge(&enveloped_tx, gas_limit);
119
120 additional_cost = additional_cost.saturating_add(operator_fee_charge);
121 }
122
123 let tx_caller = context.tx().caller();
124
125 let account = context.journal().load_account_code(tx_caller)?;
127 let account = account.data.info.clone();
128
129 validate_tx_against_account(&account, context, additional_cost)?;
130 Ok(())
131 }
132
133 fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
134 let spec = evm.ctx().cfg().spec();
136 if evm.ctx().tx().tx_type() != DEPOSIT_TRANSACTION_TYPE {
137 let l1_block_info: crate::L1BlockInfo =
138 super::L1BlockInfo::try_fetch(evm.ctx().db(), spec)?;
139
140 *evm.ctx().chain() = l1_block_info;
142 }
143
144 self.mainnet.load_accounts(evm)
145 }
146
147 fn deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
148 let ctx = evm.ctx();
149 let spec = ctx.cfg().spec();
150 let caller = ctx.tx().caller();
151 let is_deposit = ctx.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE;
152
153 let mut tx_l1_cost = U256::ZERO;
157 if is_deposit {
158 let tx = ctx.tx();
159 if let Some(mint) = tx.mint() {
160 let mut caller_account = ctx.journal().load_account(caller)?;
161 caller_account.info.balance += U256::from(mint);
162 }
163 } else {
164 let enveloped_tx = ctx
165 .tx()
166 .enveloped_tx()
167 .expect("all not deposit tx have enveloped tx")
168 .clone();
169 let spec = ctx.cfg().spec();
170 tx_l1_cost = ctx.chain().calculate_tx_l1_cost(&enveloped_tx, spec);
171 }
172
173 self.mainnet.deduct_caller(evm)?;
176
177 if !is_deposit {
181 let ctx = evm.ctx();
182
183 let gas_limit = U256::from(ctx.tx().gas_limit());
185 let enveloped_tx = ctx
186 .tx()
187 .enveloped_tx()
188 .expect("all not deposit tx have enveloped tx")
189 .clone();
190
191 let mut operator_fee_charge = U256::ZERO;
192 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
193 operator_fee_charge = ctx.chain().operator_fee_charge(&enveloped_tx, gas_limit);
194 }
195
196 let mut caller_account = ctx.journal().load_account(caller)?;
197
198 caller_account.info.balance = caller_account
199 .info
200 .balance
201 .saturating_sub(tx_l1_cost.saturating_add(operator_fee_charge));
202 }
203 Ok(())
204 }
205
206 fn last_frame_result(
207 &self,
208 evm: &mut Self::Evm,
209 frame_result: &mut <Self::Frame as Frame>::FrameResult,
210 ) -> Result<(), Self::Error> {
211 let ctx = evm.ctx();
212 let tx = ctx.tx();
213 let is_deposit = tx.tx_type() == DEPOSIT_TRANSACTION_TYPE;
214 let tx_gas_limit = tx.gas_limit();
215 let is_regolith = ctx.cfg().spec().is_enabled_in(OpSpecId::REGOLITH);
216
217 let instruction_result = frame_result.interpreter_result().result;
218 let gas = frame_result.gas_mut();
219 let remaining = gas.remaining();
220 let refunded = gas.refunded();
221
222 *gas = Gas::new_spent(tx_gas_limit);
224
225 if instruction_result.is_ok() {
226 if !is_deposit || is_regolith {
240 gas.erase_cost(remaining);
243 gas.record_refund(refunded);
244 } else if is_deposit {
245 let tx = ctx.tx();
246 if tx.is_system_transaction() {
247 gas.erase_cost(tx_gas_limit);
250 }
251 }
252 } else if instruction_result.is_revert() {
253 if !is_deposit || is_regolith {
266 gas.erase_cost(remaining);
267 }
268 }
269 Ok(())
270 }
271
272 fn reimburse_caller(
273 &self,
274 evm: &mut Self::Evm,
275 exec_result: &mut <Self::Frame as Frame>::FrameResult,
276 ) -> Result<(), Self::Error> {
277 self.mainnet.reimburse_caller(evm, exec_result)?;
278
279 let context = evm.ctx();
280 if context.tx().source_hash().is_zero() {
281 let caller = context.tx().caller();
282 let spec = context.cfg().spec();
283 let operator_fee_refund = context.chain().operator_fee_refund(exec_result.gas(), spec);
284
285 let caller_account = context.journal().load_account(caller)?;
286
287 caller_account.data.info.balance = caller_account
290 .data
291 .info
292 .balance
293 .saturating_add(operator_fee_refund);
294 }
295
296 Ok(())
297 }
298
299 fn refund(
300 &self,
301 evm: &mut Self::Evm,
302 exec_result: &mut <Self::Frame as Frame>::FrameResult,
303 eip7702_refund: i64,
304 ) {
305 exec_result.gas_mut().record_refund(eip7702_refund);
306
307 let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE;
308 let is_regolith = evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH);
309
310 let is_gas_refund_disabled = is_deposit && !is_regolith;
312 if !is_gas_refund_disabled {
313 exec_result.gas_mut().set_final_refund(
314 evm.ctx()
315 .cfg()
316 .spec()
317 .into_eth_spec()
318 .is_enabled_in(SpecId::LONDON),
319 );
320 }
321 }
322
323 fn reward_beneficiary(
324 &self,
325 evm: &mut Self::Evm,
326 exec_result: &mut <Self::Frame as Frame>::FrameResult,
327 ) -> Result<(), Self::Error> {
328 let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE;
329
330 if !is_deposit {
332 self.mainnet.reward_beneficiary(evm, exec_result)?;
333 let basefee = evm.ctx().block().basefee() as u128;
334
335 let ctx = evm.ctx();
338 let enveloped = ctx.tx().enveloped_tx().cloned();
339 let spec = ctx.cfg().spec();
340 let l1_block_info = ctx.chain();
341
342 let Some(enveloped_tx) = &enveloped else {
343 return Err(ERROR::from_string(
344 "[OPTIMISM] Failed to load enveloped transaction.".into(),
345 ));
346 };
347
348 let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, spec);
349 let mut operator_fee_cost = U256::ZERO;
350 if spec.is_enabled_in(OpSpecId::ISTHMUS) {
351 operator_fee_cost = l1_block_info.operator_fee_charge(
352 enveloped_tx,
353 U256::from(exec_result.gas().spent() - exec_result.gas().refunded() as u64),
354 );
355 }
356 let mut l1_fee_vault_account = ctx.journal().load_account(L1_FEE_RECIPIENT)?;
358 l1_fee_vault_account.mark_touch();
359 l1_fee_vault_account.info.balance += l1_cost;
360
361 let mut base_fee_vault_account =
363 evm.ctx().journal().load_account(BASE_FEE_RECIPIENT)?;
364 base_fee_vault_account.mark_touch();
365 base_fee_vault_account.info.balance += U256::from(basefee.saturating_mul(
366 (exec_result.gas().spent() - exec_result.gas().refunded() as u64) as u128,
367 ));
368
369 let mut operator_fee_vault_account =
371 evm.ctx().journal().load_account(OPERATOR_FEE_RECIPIENT)?;
372
373 operator_fee_vault_account.mark_touch();
374 operator_fee_vault_account.data.info.balance += operator_fee_cost;
375 }
376 Ok(())
377 }
378
379 fn output(
380 &self,
381 evm: &mut Self::Evm,
382 result: <Self::Frame as Frame>::FrameResult,
383 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
384 let result = self.mainnet.output(evm, result)?;
385 let result = result.map_haltreason(OpHaltReason::Base);
386 if result.result.is_halt() {
387 let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE;
391 if is_deposit && evm.ctx().cfg().spec().is_enabled_in(OpSpecId::REGOLITH) {
392 return Err(ERROR::from(OpTransactionError::HaltedDepositPostRegolith));
393 }
394 }
395 Ok(result)
396 }
397
398 fn end(
399 &self,
400 evm: &mut Self::Evm,
401 end_output: Result<ResultAndState<Self::HaltReason>, Self::Error>,
402 ) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
403 let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE;
406 end_output.or_else(|err| {
407 if err.is_tx_error() && is_deposit {
408 let ctx = evm.ctx();
409 let spec = ctx.cfg().spec();
410 let tx = ctx.tx();
411 let caller = tx.caller();
412 let mint = tx.mint();
413 let is_system_tx = tx.is_system_transaction();
414 let gas_limit = tx.gas_limit();
415 let account = {
425 let mut acc = Account::from(
426 evm.ctx()
427 .db()
428 .basic(caller)
429 .unwrap_or_default()
430 .unwrap_or_default(),
431 );
432 acc.info.nonce = acc.info.nonce.saturating_add(1);
433 acc.info.balance = acc
434 .info
435 .balance
436 .saturating_add(U256::from(mint.unwrap_or_default()));
437 acc.mark_touch();
438 acc
439 };
440 let state = HashMap::from_iter([(caller, account)]);
441
442 let gas_used = if spec.is_enabled_in(OpSpecId::REGOLITH) || !is_system_tx {
447 gas_limit
448 } else {
449 0
450 };
451
452 Ok(ResultAndState {
453 result: ExecutionResult::Halt {
454 reason: OpHaltReason::FailedDeposit,
455 gas_used,
456 },
457 state,
458 })
459 } else {
460 Err(err)
461 }
462 })
463 }
464
465 fn clear(&self, evm: &mut Self::Evm) {
466 evm.ctx().chain().clear_tx_l1_cost();
467 self.mainnet.clear(evm);
468 }
469}
470
471impl<EVM, ERROR, FRAME> InspectorHandler for OpHandler<EVM, ERROR, FRAME>
472where
473 EVM: InspectorEvmTr<
474 Context: ContextTr<
475 Journal: Journal<FinalOutput = (EvmState, Vec<Log>)>,
476 Tx: OpTxTr,
477 Cfg: Cfg<Spec = OpSpecId>,
478 Chain = L1BlockInfo,
479 >,
480 Inspector: Inspector<<<Self as Handler>::Evm as EvmTr>::Context, EthInterpreter>,
481 >,
482 ERROR: EvmTrError<EVM> + From<OpTransactionError> + FromStringError + IsTxError,
483 FRAME: InspectorFrame<
486 Evm = EVM,
487 Error = ERROR,
488 FrameResult = FrameResult,
489 FrameInit = FrameInput,
490 IT = EthInterpreter,
491 >,
492{
493 type IT = EthInterpreter;
494}
495
496#[cfg(test)]
497mod tests {
498 use crate::{DefaultOp, OpBuilder, OpContext};
499
500 use super::*;
501 use database::InMemoryDB;
502 use revm::{
503 context::Context,
504 context_interface::result::InvalidTransaction,
505 database_interface::EmptyDB,
506 handler::EthFrame,
507 interpreter::{CallOutcome, InstructionResult, InterpreterResult},
508 primitives::{bytes, Address, Bytes, B256},
509 state::AccountInfo,
510 };
511 use std::boxed::Box;
512
513 fn call_last_frame_return(
515 ctx: OpContext<EmptyDB>,
516 instruction_result: InstructionResult,
517 gas: Gas,
518 ) -> Gas {
519 let mut evm = ctx.build_op();
520
521 let mut exec_result = FrameResult::Call(CallOutcome::new(
522 InterpreterResult {
523 result: instruction_result,
524 output: Bytes::new(),
525 gas,
526 },
527 0..0,
528 ));
529
530 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
531
532 handler
533 .last_frame_result(&mut evm, &mut exec_result)
534 .unwrap();
535 handler.refund(&mut evm, &mut exec_result, 0);
536 *exec_result.gas()
537 }
538
539 #[test]
540 fn test_revert_gas() {
541 let ctx = Context::op()
542 .modify_tx_chained(|tx| {
543 tx.base.gas_limit = 100;
544 tx.enveloped_tx = None;
545 })
546 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK);
547
548 let gas = call_last_frame_return(ctx, InstructionResult::Revert, Gas::new(90));
549 assert_eq!(gas.remaining(), 90);
550 assert_eq!(gas.spent(), 10);
551 assert_eq!(gas.refunded(), 0);
552 }
553
554 #[test]
555 fn test_consume_gas() {
556 let ctx = Context::op()
557 .modify_tx_chained(|tx| {
558 tx.base.gas_limit = 100;
559 tx.deposit.source_hash = B256::ZERO;
560 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
561 })
562 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
563
564 let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90));
565 assert_eq!(gas.remaining(), 90);
566 assert_eq!(gas.spent(), 10);
567 assert_eq!(gas.refunded(), 0);
568 }
569
570 #[test]
571 fn test_consume_gas_with_refund() {
572 let ctx = Context::op()
573 .modify_tx_chained(|tx| {
574 tx.base.gas_limit = 100;
575 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
576 tx.deposit.source_hash = B256::ZERO;
577 })
578 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
579
580 let mut ret_gas = Gas::new(90);
581 ret_gas.record_refund(20);
582
583 let gas = call_last_frame_return(ctx.clone(), InstructionResult::Stop, ret_gas);
584 assert_eq!(gas.remaining(), 90);
585 assert_eq!(gas.spent(), 10);
586 assert_eq!(gas.refunded(), 2); let gas = call_last_frame_return(ctx, InstructionResult::Revert, ret_gas);
589 assert_eq!(gas.remaining(), 90);
590 assert_eq!(gas.spent(), 10);
591 assert_eq!(gas.refunded(), 0);
592 }
593
594 #[test]
595 fn test_consume_gas_sys_deposit_tx() {
596 let ctx = Context::op()
597 .modify_tx_chained(|tx| {
598 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
599 tx.base.gas_limit = 100;
600 tx.deposit.source_hash = B256::ZERO;
601 })
602 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK);
603 let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90));
604 assert_eq!(gas.remaining(), 0);
605 assert_eq!(gas.spent(), 100);
606 assert_eq!(gas.refunded(), 0);
607 }
608
609 #[test]
610 fn test_commit_mint_value() {
611 let caller = Address::ZERO;
612 let mut db = InMemoryDB::default();
613 db.insert_account_info(
614 caller,
615 AccountInfo {
616 balance: U256::from(1000),
617 ..Default::default()
618 },
619 );
620
621 let mut ctx = Context::op()
622 .with_db(db)
623 .with_chain(L1BlockInfo {
624 l1_base_fee: U256::from(1_000),
625 l1_fee_overhead: Some(U256::from(1_000)),
626 l1_base_fee_scalar: U256::from(1_000),
627 ..Default::default()
628 })
629 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
630 ctx.modify_tx(|tx| {
631 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
632 tx.deposit.source_hash = B256::ZERO;
633 tx.deposit.mint = Some(10);
634 });
635
636 let mut evm = ctx.build_op();
637
638 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
639 handler.deduct_caller(&mut evm).unwrap();
640
641 let account = evm.ctx().journal().load_account(caller).unwrap();
643 assert_eq!(account.info.balance, U256::from(1010));
644 }
645
646 #[test]
647 fn test_remove_l1_cost_non_deposit() {
648 let caller = Address::ZERO;
649 let mut db = InMemoryDB::default();
650 db.insert_account_info(
651 caller,
652 AccountInfo {
653 balance: U256::from(1000),
654 ..Default::default()
655 },
656 );
657 let ctx = Context::op()
658 .with_db(db)
659 .with_chain(L1BlockInfo {
660 l1_base_fee: U256::from(1_000),
661 l1_fee_overhead: Some(U256::from(1_000)),
662 l1_base_fee_scalar: U256::from(1_000),
663 ..Default::default()
664 })
665 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH)
666 .modify_tx_chained(|tx| {
667 tx.base.gas_limit = 100;
668 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
669 tx.deposit.mint = Some(10);
670 tx.enveloped_tx = Some(bytes!("FACADE"));
671 tx.deposit.source_hash = B256::ZERO;
672 });
673
674 let mut evm = ctx.build_op();
675
676 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
677 handler.deduct_caller(&mut evm).unwrap();
678
679 let account = evm.ctx().journal().load_account(caller).unwrap();
681 assert_eq!(account.info.balance, U256::from(1010));
682 }
683
684 #[test]
685 fn test_remove_l1_cost() {
686 let caller = Address::ZERO;
687 let mut db = InMemoryDB::default();
688 db.insert_account_info(
689 caller,
690 AccountInfo {
691 balance: U256::from(1049),
692 ..Default::default()
693 },
694 );
695 let ctx = Context::op()
696 .with_db(db)
697 .with_chain(L1BlockInfo {
698 l1_base_fee: U256::from(1_000),
699 l1_fee_overhead: Some(U256::from(1_000)),
700 l1_base_fee_scalar: U256::from(1_000),
701 ..Default::default()
702 })
703 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH)
704 .modify_tx_chained(|tx| {
705 tx.base.gas_limit = 100;
706 tx.deposit.source_hash = B256::ZERO;
707 tx.enveloped_tx = Some(bytes!("FACADE"));
708 });
709
710 let mut evm = ctx.build_op();
711 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
712
713 handler.deduct_caller(&mut evm).unwrap();
715
716 let account = evm.ctx().journal().load_account(caller).unwrap();
718 assert_eq!(account.info.balance, U256::from(1));
719 }
720
721 #[test]
722 fn test_remove_operator_cost() {
723 let caller = Address::ZERO;
724 let mut db = InMemoryDB::default();
725 db.insert_account_info(
726 caller,
727 AccountInfo {
728 balance: U256::from(151),
729 ..Default::default()
730 },
731 );
732 let ctx = Context::op()
733 .with_db(db)
734 .with_chain(L1BlockInfo {
735 operator_fee_scalar: Some(U256::from(10_000_000)),
736 operator_fee_constant: Some(U256::from(50)),
737 ..Default::default()
738 })
739 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS)
740 .modify_tx_chained(|tx| {
741 tx.base.gas_limit = 10;
742 tx.enveloped_tx = Some(bytes!("FACADE"));
743 });
744
745 let mut evm = ctx.build_op();
746 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
747
748 handler.deduct_caller(&mut evm).unwrap();
751
752 let account = evm.ctx().journal().load_account(caller).unwrap();
754 assert_eq!(account.info.balance, U256::from(1));
755 }
756
757 #[test]
758 fn test_remove_l1_cost_lack_of_funds() {
759 let caller = Address::ZERO;
760 let mut db = InMemoryDB::default();
761 db.insert_account_info(
762 caller,
763 AccountInfo {
764 balance: U256::from(48),
765 ..Default::default()
766 },
767 );
768 let ctx = Context::op()
769 .with_db(db)
770 .with_chain(L1BlockInfo {
771 l1_base_fee: U256::from(1_000),
772 l1_fee_overhead: Some(U256::from(1_000)),
773 l1_base_fee_scalar: U256::from(1_000),
774 ..Default::default()
775 })
776 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH)
777 .modify_tx_chained(|tx| {
778 tx.enveloped_tx = Some(bytes!("FACADE"));
779 });
780
781 let mut evm = ctx.build_op();
783 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
784
785 assert_eq!(
787 handler.validate_tx_against_state(&mut evm),
788 Err(EVMError::Transaction(
789 InvalidTransaction::LackOfFundForMaxFee {
790 fee: Box::new(U256::from(1048)),
791 balance: Box::new(U256::from(48)),
792 }
793 .into(),
794 ))
795 );
796 }
797
798 #[test]
799 fn test_validate_sys_tx() {
800 let ctx = Context::op()
802 .modify_tx_chained(|tx| {
803 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
804 tx.deposit.is_system_transaction = true;
805 })
806 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
807
808 let mut evm = ctx.build_op();
809 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
810
811 assert_eq!(
812 handler.validate_env(&mut evm),
813 Err(EVMError::Transaction(
814 OpTransactionError::DepositSystemTxPostRegolith
815 ))
816 );
817
818 evm.ctx().modify_cfg(|cfg| cfg.spec = OpSpecId::BEDROCK);
819
820 assert!(handler.validate_env(&mut evm).is_ok());
822 }
823
824 #[test]
825 fn test_validate_deposit_tx() {
826 let ctx = Context::op()
828 .modify_tx_chained(|tx| {
829 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
830 tx.deposit.source_hash = B256::ZERO;
831 })
832 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
833
834 let mut evm = ctx.build_op();
835 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
836
837 assert!(handler.validate_env(&mut evm).is_ok());
838 }
839
840 #[test]
841 fn test_validate_tx_against_state_deposit_tx() {
842 let ctx = Context::op()
844 .modify_tx_chained(|tx| {
845 tx.base.tx_type = DEPOSIT_TRANSACTION_TYPE;
846 tx.deposit.source_hash = B256::ZERO;
847 })
848 .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH);
849
850 let mut evm = ctx.build_op();
851 let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame<_, _, _>>::new();
852
853 assert!(handler.validate_env(&mut evm).is_ok());
855 }
856}