github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/test/L1/OptimismPortal.t.sol (about)

     1  // SPDX-License-Identifier: MIT
     2  pragma solidity 0.8.15;
     3  
     4  // Testing utilities
     5  import { stdError } from "forge-std/Test.sol";
     6  
     7  import { CommonTest } from "test/setup/CommonTest.sol";
     8  import { NextImpl } from "test/mocks/NextImpl.sol";
     9  import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
    10  
    11  // Libraries
    12  import { Types } from "src/libraries/Types.sol";
    13  import { Hashing } from "src/libraries/Hashing.sol";
    14  import { Constants } from "src/libraries/Constants.sol";
    15  
    16  // Target contract dependencies
    17  import { Proxy } from "src/universal/Proxy.sol";
    18  import { ResourceMetering } from "src/L1/ResourceMetering.sol";
    19  import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol";
    20  import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
    21  import { SystemConfig } from "src/L1/SystemConfig.sol";
    22  import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
    23  import { OptimismPortal } from "src/L1/OptimismPortal.sol";
    24  
    25  contract OptimismPortal_Test is CommonTest {
    26      address depositor;
    27  
    28      /// @notice Marked virtual to be overridden in
    29      ///         test/kontrol/deployment/DeploymentSummary.t.sol
    30      function setUp() public virtual override {
    31          super.setUp();
    32          depositor = makeAddr("depositor");
    33      }
    34  
    35      /// @dev Tests that the constructor sets the correct values.
    36      /// @notice Marked virtual to be overridden in
    37      ///         test/kontrol/deployment/DeploymentSummary.t.sol
    38      function test_constructor_succeeds() external virtual {
    39          OptimismPortal opImpl = OptimismPortal(payable(deploy.mustGetAddress("OptimismPortal")));
    40          assertEq(address(opImpl.L2_ORACLE()), address(0));
    41          assertEq(address(opImpl.l2Oracle()), address(0));
    42          assertEq(address(opImpl.SYSTEM_CONFIG()), address(0));
    43          assertEq(address(opImpl.systemConfig()), address(0));
    44          assertEq(address(opImpl.superchainConfig()), address(0));
    45          assertEq(opImpl.l2Sender(), Constants.DEFAULT_L2_SENDER);
    46          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = opImpl.params();
    47          assertEq(prevBaseFee, 1 gwei);
    48          assertEq(prevBoughtGas, 0);
    49          assertEq(prevBlockNum, uint64(block.number));
    50      }
    51  
    52      /// @dev Tests that the initializer sets the correct values.
    53      /// @notice Marked virtual to be overridden in
    54      ///         test/kontrol/deployment/DeploymentSummary.t.sol
    55      function test_initialize_succeeds() external virtual {
    56          address guardian = deploy.cfg().superchainConfigGuardian();
    57          assertEq(address(optimismPortal.L2_ORACLE()), address(l2OutputOracle));
    58          assertEq(address(optimismPortal.l2Oracle()), address(l2OutputOracle));
    59          assertEq(address(optimismPortal.SYSTEM_CONFIG()), address(systemConfig));
    60          assertEq(address(optimismPortal.systemConfig()), address(systemConfig));
    61          assertEq(optimismPortal.GUARDIAN(), guardian);
    62          assertEq(optimismPortal.guardian(), guardian);
    63          assertEq(address(optimismPortal.superchainConfig()), address(superchainConfig));
    64          assertEq(optimismPortal.l2Sender(), Constants.DEFAULT_L2_SENDER);
    65          assertEq(optimismPortal.paused(), false);
    66          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal.params();
    67          assertEq(prevBaseFee, 1 gwei);
    68          assertEq(prevBoughtGas, 0);
    69          assertEq(prevBlockNum, uint64(block.number));
    70      }
    71  
    72      /// @dev Tests that `pause` successfully pauses
    73      ///      when called by the GUARDIAN.
    74      function test_pause_succeeds() external {
    75          address guardian = optimismPortal.GUARDIAN();
    76  
    77          assertEq(optimismPortal.paused(), false);
    78  
    79          vm.expectEmit(address(superchainConfig));
    80          emit Paused("identifier");
    81  
    82          vm.prank(guardian);
    83          superchainConfig.pause("identifier");
    84  
    85          assertEq(optimismPortal.paused(), true);
    86      }
    87  
    88      /// @dev Tests that `pause` reverts when called by a non-GUARDIAN.
    89      function test_pause_onlyGuardian_reverts() external {
    90          assertEq(optimismPortal.paused(), false);
    91  
    92          assertTrue(optimismPortal.GUARDIAN() != alice);
    93          vm.expectRevert("SuperchainConfig: only guardian can pause");
    94          vm.prank(alice);
    95          superchainConfig.pause("identifier");
    96  
    97          assertEq(optimismPortal.paused(), false);
    98      }
    99  
   100      /// @dev Tests that `unpause` successfully unpauses
   101      ///      when called by the GUARDIAN.
   102      function test_unpause_succeeds() external {
   103          address guardian = optimismPortal.GUARDIAN();
   104  
   105          vm.prank(guardian);
   106          superchainConfig.pause("identifier");
   107          assertEq(optimismPortal.paused(), true);
   108  
   109          vm.expectEmit(address(superchainConfig));
   110          emit Unpaused();
   111          vm.prank(guardian);
   112          superchainConfig.unpause();
   113  
   114          assertEq(optimismPortal.paused(), false);
   115      }
   116  
   117      /// @dev Tests that `unpause` reverts when called by a non-GUARDIAN.
   118      function test_unpause_onlyGuardian_reverts() external {
   119          address guardian = optimismPortal.GUARDIAN();
   120  
   121          vm.prank(guardian);
   122          superchainConfig.pause("identifier");
   123          assertEq(optimismPortal.paused(), true);
   124  
   125          assertTrue(optimismPortal.GUARDIAN() != alice);
   126          vm.expectRevert("SuperchainConfig: only guardian can unpause");
   127          vm.prank(alice);
   128          superchainConfig.unpause();
   129  
   130          assertEq(optimismPortal.paused(), true);
   131      }
   132  
   133      /// @dev Tests that `receive` successdully deposits ETH.
   134      function testFuzz_receive_succeeds(uint256 _value) external {
   135          vm.expectEmit(address(optimismPortal));
   136          emitTransactionDeposited({
   137              _from: alice,
   138              _to: alice,
   139              _value: _value,
   140              _mint: _value,
   141              _gasLimit: 100_000,
   142              _isCreation: false,
   143              _data: hex""
   144          });
   145  
   146          // give alice money and send as an eoa
   147          vm.deal(alice, _value);
   148          vm.prank(alice, alice);
   149          (bool s,) = address(optimismPortal).call{ value: _value }(hex"");
   150  
   151          assertTrue(s);
   152          assertEq(address(optimismPortal).balance, _value);
   153      }
   154  
   155      /// @dev Tests that `depositTransaction` reverts when the destination address is non-zero
   156      ///      for a contract creation deposit.
   157      function test_depositTransaction_contractCreation_reverts() external {
   158          // contract creation must have a target of address(0)
   159          vm.expectRevert("OptimismPortal: must send to address(0) when creating a contract");
   160          optimismPortal.depositTransaction(address(1), 1, 0, true, hex"");
   161      }
   162  
   163      /// @dev Tests that `depositTransaction` reverts when the data is too large.
   164      ///      This places an upper bound on unsafe blocks sent over p2p.
   165      function test_depositTransaction_largeData_reverts() external {
   166          uint256 size = 120_001;
   167          uint64 gasLimit = optimismPortal.minimumGasLimit(uint64(size));
   168          vm.expectRevert("OptimismPortal: data too large");
   169          optimismPortal.depositTransaction({
   170              _to: address(0),
   171              _value: 0,
   172              _gasLimit: gasLimit,
   173              _isCreation: false,
   174              _data: new bytes(size)
   175          });
   176      }
   177  
   178      /// @dev Tests that `depositTransaction` reverts when the gas limit is too small.
   179      function test_depositTransaction_smallGasLimit_reverts() external {
   180          vm.expectRevert("OptimismPortal: gas limit too small");
   181          optimismPortal.depositTransaction({ _to: address(1), _value: 0, _gasLimit: 0, _isCreation: false, _data: hex"" });
   182      }
   183  
   184      /// @dev Tests that `depositTransaction` succeeds for small,
   185      ///      but sufficient, gas limits.
   186      function testFuzz_depositTransaction_smallGasLimit_succeeds(bytes memory _data, bool _shouldFail) external {
   187          uint64 gasLimit = optimismPortal.minimumGasLimit(uint64(_data.length));
   188          if (_shouldFail) {
   189              gasLimit = uint64(bound(gasLimit, 0, gasLimit - 1));
   190              vm.expectRevert("OptimismPortal: gas limit too small");
   191          }
   192  
   193          optimismPortal.depositTransaction({
   194              _to: address(0x40),
   195              _value: 0,
   196              _gasLimit: gasLimit,
   197              _isCreation: false,
   198              _data: _data
   199          });
   200      }
   201  
   202      /// @dev Tests that `minimumGasLimit` succeeds for small calldata sizes.
   203      ///      The gas limit should be 21k for 0 calldata and increase linearly
   204      ///      for larger calldata sizes.
   205      function test_minimumGasLimit_succeeds() external {
   206          assertEq(optimismPortal.minimumGasLimit(0), 21_000);
   207          assertTrue(optimismPortal.minimumGasLimit(2) > optimismPortal.minimumGasLimit(1));
   208          assertTrue(optimismPortal.minimumGasLimit(3) > optimismPortal.minimumGasLimit(2));
   209      }
   210  
   211      /// @dev Tests that `depositTransaction` succeeds for an EOA.
   212      function testFuzz_depositTransaction_eoa_succeeds(
   213          address _to,
   214          uint64 _gasLimit,
   215          uint256 _value,
   216          uint256 _mint,
   217          bool _isCreation,
   218          bytes memory _data
   219      )
   220          external
   221      {
   222          _gasLimit = uint64(
   223              bound(
   224                  _gasLimit,
   225                  optimismPortal.minimumGasLimit(uint64(_data.length)),
   226                  systemConfig.resourceConfig().maxResourceLimit
   227              )
   228          );
   229          if (_isCreation) _to = address(0);
   230  
   231          // EOA emulation
   232          vm.expectEmit(address(optimismPortal));
   233          emitTransactionDeposited({
   234              _from: depositor,
   235              _to: _to,
   236              _value: _value,
   237              _mint: _mint,
   238              _gasLimit: _gasLimit,
   239              _isCreation: _isCreation,
   240              _data: _data
   241          });
   242  
   243          vm.deal(depositor, _mint);
   244          vm.prank(depositor, depositor);
   245          optimismPortal.depositTransaction{ value: _mint }({
   246              _to: _to,
   247              _value: _value,
   248              _gasLimit: _gasLimit,
   249              _isCreation: _isCreation,
   250              _data: _data
   251          });
   252          assertEq(address(optimismPortal).balance, _mint);
   253      }
   254  
   255      /// @dev Tests that `depositTransaction` succeeds for a contract.
   256      function testFuzz_depositTransaction_contract_succeeds(
   257          address _to,
   258          uint64 _gasLimit,
   259          uint256 _value,
   260          uint256 _mint,
   261          bool _isCreation,
   262          bytes memory _data
   263      )
   264          external
   265      {
   266          _gasLimit = uint64(
   267              bound(
   268                  _gasLimit,
   269                  optimismPortal.minimumGasLimit(uint64(_data.length)),
   270                  systemConfig.resourceConfig().maxResourceLimit
   271              )
   272          );
   273          if (_isCreation) _to = address(0);
   274  
   275          vm.expectEmit(address(optimismPortal));
   276          emitTransactionDeposited({
   277              _from: AddressAliasHelper.applyL1ToL2Alias(address(this)),
   278              _to: _to,
   279              _value: _value,
   280              _mint: _mint,
   281              _gasLimit: _gasLimit,
   282              _isCreation: _isCreation,
   283              _data: _data
   284          });
   285  
   286          vm.deal(address(this), _mint);
   287          vm.prank(address(this));
   288          optimismPortal.depositTransaction{ value: _mint }({
   289              _to: _to,
   290              _value: _value,
   291              _gasLimit: _gasLimit,
   292              _isCreation: _isCreation,
   293              _data: _data
   294          });
   295          assertEq(address(optimismPortal).balance, _mint);
   296      }
   297  
   298      /// @dev Tests that `isOutputFinalized` succeeds for an EOA depositing a tx with ETH and data.
   299      /// @notice Marked virtual to be overridden in
   300      ///         test/kontrol/deployment/DeploymentSummary.t.sol
   301      function test_simple_isOutputFinalized_succeeds() external virtual {
   302          uint256 startingBlockNumber = deploy.cfg().l2OutputOracleStartingBlockNumber();
   303  
   304          uint256 ts = block.timestamp;
   305          vm.mockCall(
   306              address(optimismPortal.l2Oracle()),
   307              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   308              abi.encode(Types.OutputProposal(bytes32(uint256(1)), uint128(ts), uint128(startingBlockNumber)))
   309          );
   310  
   311          // warp to the finalization period
   312          vm.warp(ts + l2OutputOracle.FINALIZATION_PERIOD_SECONDS());
   313          assertEq(optimismPortal.isOutputFinalized(0), false);
   314  
   315          // warp past the finalization period
   316          vm.warp(ts + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   317          assertEq(optimismPortal.isOutputFinalized(0), true);
   318      }
   319  
   320      /// @dev Tests `isOutputFinalized` for a finalized output.
   321      /// @notice Marked virtual to be overridden in
   322      ///         test/kontrol/deployment/DeploymentSummary.t.sol
   323      function test_isOutputFinalized_succeeds() external virtual {
   324          uint256 checkpoint = l2OutputOracle.nextBlockNumber();
   325          uint256 nextOutputIndex = l2OutputOracle.nextOutputIndex();
   326          vm.roll(checkpoint);
   327          vm.warp(l2OutputOracle.computeL2Timestamp(checkpoint) + 1);
   328          vm.prank(l2OutputOracle.PROPOSER());
   329          l2OutputOracle.proposeL2Output(keccak256(abi.encode(2)), checkpoint, 0, 0);
   330  
   331          // warp to the final second of the finalization period
   332          uint256 finalizationHorizon = block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS();
   333          vm.warp(finalizationHorizon);
   334          // The checkpointed block should not be finalized until 1 second from now.
   335          assertEq(optimismPortal.isOutputFinalized(nextOutputIndex), false);
   336          // Nor should a block after it
   337          vm.expectRevert(stdError.indexOOBError);
   338          assertEq(optimismPortal.isOutputFinalized(nextOutputIndex + 1), false);
   339          // warp past the finalization period
   340          vm.warp(finalizationHorizon + 1);
   341          // It should now be finalized.
   342          assertEq(optimismPortal.isOutputFinalized(nextOutputIndex), true);
   343          // But not the block after it.
   344          vm.expectRevert(stdError.indexOOBError);
   345          assertEq(optimismPortal.isOutputFinalized(nextOutputIndex + 1), false);
   346      }
   347  }
   348  
   349  contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
   350      // Reusable default values for a test withdrawal
   351      Types.WithdrawalTransaction _defaultTx;
   352  
   353      uint256 _proposedOutputIndex;
   354      uint256 _proposedBlockNumber;
   355      bytes32 _stateRoot;
   356      bytes32 _storageRoot;
   357      bytes32 _outputRoot;
   358      bytes32 _withdrawalHash;
   359      bytes[] _withdrawalProof;
   360      Types.OutputRootProof internal _outputRootProof;
   361  
   362      // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls.
   363      constructor() {
   364          super.setUp();
   365          _defaultTx = Types.WithdrawalTransaction({
   366              nonce: 0,
   367              sender: alice,
   368              target: bob,
   369              value: 100,
   370              gasLimit: 100_000,
   371              data: hex""
   372          });
   373          // Get withdrawal proof data we can use for testing.
   374          (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) =
   375              ffi.getProveWithdrawalTransactionInputs(_defaultTx);
   376  
   377          // Setup a dummy output root proof for reuse.
   378          _outputRootProof = Types.OutputRootProof({
   379              version: bytes32(uint256(0)),
   380              stateRoot: _stateRoot,
   381              messagePasserStorageRoot: _storageRoot,
   382              latestBlockhash: bytes32(uint256(0))
   383          });
   384          _proposedBlockNumber = l2OutputOracle.nextBlockNumber();
   385          _proposedOutputIndex = l2OutputOracle.nextOutputIndex();
   386      }
   387  
   388      /// @dev Setup the system for a ready-to-use state.
   389      function setUp() public override {
   390          // Configure the oracle to return the output root we've prepared.
   391          vm.warp(l2OutputOracle.computeL2Timestamp(_proposedBlockNumber) + 1);
   392          vm.prank(l2OutputOracle.PROPOSER());
   393          l2OutputOracle.proposeL2Output(_outputRoot, _proposedBlockNumber, 0, 0);
   394  
   395          // Warp beyond the finalization period for the block we've proposed.
   396          vm.warp(
   397              l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS()
   398                  + 1
   399          );
   400          // Fund the portal so that we can withdraw ETH.
   401          vm.deal(address(optimismPortal), 0xFFFFFFFF);
   402      }
   403  
   404      /// @dev Asserts that the reentrant call will revert.
   405      function callPortalAndExpectRevert() external payable {
   406          vm.expectRevert("OptimismPortal: can only trigger one withdrawal per transaction");
   407          // Arguments here don't matter, as the require check is the first thing that happens.
   408          // We assume that this has already been proven.
   409          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   410          // Assert that the withdrawal was not finalized.
   411          assertFalse(optimismPortal.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
   412      }
   413  
   414      /// @dev Tests that `proveWithdrawalTransaction` reverts when paused.
   415      function test_proveWithdrawalTransaction_paused_reverts() external {
   416          vm.prank(optimismPortal.GUARDIAN());
   417          superchainConfig.pause("identifier");
   418  
   419          vm.expectRevert("OptimismPortal: paused");
   420          optimismPortal.proveWithdrawalTransaction({
   421              _tx: _defaultTx,
   422              _l2OutputIndex: _proposedOutputIndex,
   423              _outputRootProof: _outputRootProof,
   424              _withdrawalProof: _withdrawalProof
   425          });
   426      }
   427  
   428      /// @dev Tests that `proveWithdrawalTransaction` reverts when the target is the portal contract.
   429      function test_proveWithdrawalTransaction_onSelfCall_reverts() external {
   430          _defaultTx.target = address(optimismPortal);
   431          vm.expectRevert("OptimismPortal: you cannot send messages to the portal contract");
   432          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   433      }
   434  
   435      /// @dev Tests that `proveWithdrawalTransaction` reverts when
   436      ///      the outputRootProof does not match the output root
   437      function test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() external {
   438          // Modify the version to invalidate the withdrawal proof.
   439          _outputRootProof.version = bytes32(uint256(1));
   440          vm.expectRevert("OptimismPortal: invalid output root proof");
   441          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   442      }
   443  
   444      /// @dev Tests that `proveWithdrawalTransaction` reverts when the withdrawal is missing.
   445      function test_proveWithdrawalTransaction_onInvalidWithdrawalProof_reverts() external {
   446          // modify the default test values to invalidate the proof.
   447          _defaultTx.data = hex"abcd";
   448          vm.expectRevert("MerkleTrie: path remainder must share all nibbles with key");
   449          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   450      }
   451  
   452      /// @dev Tests that `proveWithdrawalTransaction` reverts when the withdrawal has already
   453      ///      been proven.
   454      function test_proveWithdrawalTransaction_replayProve_reverts() external {
   455          vm.expectEmit(true, true, true, true);
   456          emit WithdrawalProven(_withdrawalHash, alice, bob);
   457          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   458  
   459          vm.expectRevert("OptimismPortal: withdrawal hash has already been proven");
   460          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   461      }
   462  
   463      /// @dev Tests that `proveWithdrawalTransaction` succeeds when the withdrawal has already
   464      ///      been proven and the output root has changed and the l2BlockNumber stays the same.
   465      function test_proveWithdrawalTransaction_replayProveChangedOutputRoot_succeeds() external {
   466          vm.expectEmit(true, true, true, true);
   467          emit WithdrawalProven(_withdrawalHash, alice, bob);
   468          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   469  
   470          // Compute the storage slot of the outputRoot corresponding to the `withdrawalHash`
   471          // inside of the `provenWithdrawal`s mapping.
   472          bytes32 slot;
   473          assembly {
   474              mstore(0x00, sload(_withdrawalHash.slot))
   475              mstore(0x20, 52) // 52 is the slot of the `provenWithdrawals` mapping in the OptimismPortal
   476              slot := keccak256(0x00, 0x40)
   477          }
   478  
   479          // Store a different output root within the `provenWithdrawals` mapping without
   480          // touching the l2BlockNumber or timestamp.
   481          vm.store(address(optimismPortal), slot, bytes32(0));
   482  
   483          // Warp ahead 1 second
   484          vm.warp(block.timestamp + 1);
   485  
   486          // Even though we have already proven this withdrawalHash, we should be allowed to re-submit
   487          // our proof with a changed outputRoot
   488          vm.expectEmit(true, true, true, true);
   489          emit WithdrawalProven(_withdrawalHash, alice, bob);
   490          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   491  
   492          // Ensure that the withdrawal was updated within the mapping
   493          (, uint128 timestamp,) = optimismPortal.provenWithdrawals(_withdrawalHash);
   494          assertEq(timestamp, block.timestamp);
   495      }
   496  
   497      /// @dev Tests that `proveWithdrawalTransaction` succeeds when the withdrawal has already
   498      ///      been proven and the output root, output index, and l2BlockNumber have changed.
   499      function test_proveWithdrawalTransaction_replayProveChangedOutputRootAndOutputIndex_succeeds() external {
   500          vm.expectEmit(true, true, true, true);
   501          emit WithdrawalProven(_withdrawalHash, alice, bob);
   502          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   503  
   504          // Compute the storage slot of the outputRoot corresponding to the `withdrawalHash`
   505          // inside of the `provenWithdrawal`s mapping.
   506          bytes32 slot;
   507          assembly {
   508              mstore(0x00, sload(_withdrawalHash.slot))
   509              mstore(0x20, 52) // 52 is the slot of the `provenWithdrawals` mapping in OptimismPortal
   510              slot := keccak256(0x00, 0x40)
   511          }
   512  
   513          // Store a dummy output root within the `provenWithdrawals` mapping without touching the
   514          // l2BlockNumber or timestamp.
   515          vm.store(address(optimismPortal), slot, bytes32(0));
   516  
   517          // Fetch the output proposal at `_proposedOutputIndex` from the L2OutputOracle
   518          Types.OutputProposal memory proposal = optimismPortal.l2Oracle().getL2Output(_proposedOutputIndex);
   519  
   520          // Propose the same output root again, creating the same output at a different index + l2BlockNumber.
   521          vm.startPrank(optimismPortal.l2Oracle().PROPOSER());
   522          optimismPortal.l2Oracle().proposeL2Output(
   523              proposal.outputRoot, optimismPortal.l2Oracle().nextBlockNumber(), blockhash(block.number), block.number
   524          );
   525          vm.stopPrank();
   526  
   527          // Warp ahead 1 second
   528          vm.warp(block.timestamp + 1);
   529  
   530          // Even though we have already proven this withdrawalHash, we should be allowed to re-submit
   531          // our proof with a changed outputRoot + a different output index
   532          vm.expectEmit(true, true, true, true);
   533          emit WithdrawalProven(_withdrawalHash, alice, bob);
   534          optimismPortal.proveWithdrawalTransaction(
   535              _defaultTx, _proposedOutputIndex + 1, _outputRootProof, _withdrawalProof
   536          );
   537  
   538          // Ensure that the withdrawal was updated within the mapping
   539          (, uint128 timestamp,) = optimismPortal.provenWithdrawals(_withdrawalHash);
   540          assertEq(timestamp, block.timestamp);
   541      }
   542  
   543      /// @dev Tests that `proveWithdrawalTransaction` succeeds.
   544      function test_proveWithdrawalTransaction_validWithdrawalProof_succeeds() external {
   545          vm.expectEmit(true, true, true, true);
   546          emit WithdrawalProven(_withdrawalHash, alice, bob);
   547          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   548      }
   549  
   550      /// @dev Tests that `finalizeWithdrawalTransaction` succeeds.
   551      function test_finalizeWithdrawalTransaction_provenWithdrawalHash_succeeds() external {
   552          uint256 bobBalanceBefore = address(bob).balance;
   553  
   554          vm.expectEmit(true, true, true, true);
   555          emit WithdrawalProven(_withdrawalHash, alice, bob);
   556          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   557  
   558          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   559          vm.expectEmit(true, true, false, true);
   560          emit WithdrawalFinalized(_withdrawalHash, true);
   561          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   562  
   563          assert(address(bob).balance == bobBalanceBefore + 100);
   564      }
   565  
   566      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the contract is paused.
   567      function test_finalizeWithdrawalTransaction_paused_reverts() external {
   568          vm.prank(optimismPortal.GUARDIAN());
   569          superchainConfig.pause("identifier");
   570  
   571          vm.expectRevert("OptimismPortal: paused");
   572          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   573      }
   574  
   575      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has not been
   576      function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external {
   577          uint256 bobBalanceBefore = address(bob).balance;
   578  
   579          vm.expectRevert("OptimismPortal: withdrawal has not been proven yet");
   580          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   581  
   582          assert(address(bob).balance == bobBalanceBefore);
   583      }
   584  
   585      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has not been
   586      ///      proven long enough ago.
   587      function test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() external {
   588          uint256 bobBalanceBefore = address(bob).balance;
   589  
   590          vm.expectEmit(true, true, true, true);
   591          emit WithdrawalProven(_withdrawalHash, alice, bob);
   592          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   593  
   594          // Mock a call where the resulting output root is anything but the original output root. In
   595          // this case we just use bytes32(uint256(1)).
   596          vm.mockCall(
   597              address(optimismPortal.l2Oracle()),
   598              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   599              abi.encode(bytes32(uint256(1)), _proposedBlockNumber)
   600          );
   601  
   602          vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed");
   603          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   604  
   605          assert(address(bob).balance == bobBalanceBefore);
   606      }
   607  
   608      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the provenWithdrawal's timestamp
   609      ///      is less than the L2 output oracle's starting timestamp.
   610      function test_finalizeWithdrawalTransaction_timestampLessThanL2OracleStart_reverts() external {
   611          uint256 bobBalanceBefore = address(bob).balance;
   612  
   613          // Prove our withdrawal
   614          vm.expectEmit(true, true, true, true);
   615          emit WithdrawalProven(_withdrawalHash, alice, bob);
   616          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   617  
   618          // Warp to after the finalization period
   619          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   620  
   621          // Mock a startingTimestamp change on the L2 Oracle
   622          vm.mockCall(
   623              address(optimismPortal.l2Oracle()),
   624              abi.encodeWithSignature("startingTimestamp()"),
   625              abi.encode(block.timestamp + 1)
   626          );
   627  
   628          // Attempt to finalize the withdrawal
   629          vm.expectRevert("OptimismPortal: withdrawal timestamp less than L2 Oracle starting timestamp");
   630          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   631  
   632          // Ensure that bob's balance has remained the same
   633          assertEq(bobBalanceBefore, address(bob).balance);
   634      }
   635  
   636      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the output root proven is not the
   637      ///      same as the output root at the time of finalization.
   638      function test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() external {
   639          uint256 bobBalanceBefore = address(bob).balance;
   640  
   641          // Prove our withdrawal
   642          vm.expectEmit(true, true, true, true);
   643          emit WithdrawalProven(_withdrawalHash, alice, bob);
   644          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   645  
   646          // Warp to after the finalization period
   647          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   648  
   649          // Mock an outputRoot change on the output proposal before attempting
   650          // to finalize the withdrawal.
   651          vm.mockCall(
   652              address(optimismPortal.l2Oracle()),
   653              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   654              abi.encode(
   655                  Types.OutputProposal(bytes32(uint256(0)), uint128(block.timestamp), uint128(_proposedBlockNumber))
   656              )
   657          );
   658  
   659          // Attempt to finalize the withdrawal
   660          vm.expectRevert("OptimismPortal: output root proven is not the same as current output root");
   661          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   662  
   663          // Ensure that bob's balance has remained the same
   664          assertEq(bobBalanceBefore, address(bob).balance);
   665      }
   666  
   667      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the output proposal's timestamp
   668      ///      has not passed the finalization period.
   669      function test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() external {
   670          uint256 bobBalanceBefore = address(bob).balance;
   671  
   672          // Prove our withdrawal
   673          vm.expectEmit(true, true, true, true);
   674          emit WithdrawalProven(_withdrawalHash, alice, bob);
   675          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   676  
   677          // Warp to after the finalization period
   678          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   679  
   680          // Mock a timestamp change on the output proposal that has not passed the
   681          // finalization period.
   682          vm.mockCall(
   683              address(optimismPortal.l2Oracle()),
   684              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   685              abi.encode(Types.OutputProposal(_outputRoot, uint128(block.timestamp + 1), uint128(_proposedBlockNumber)))
   686          );
   687  
   688          // Attempt to finalize the withdrawal
   689          vm.expectRevert("OptimismPortal: output proposal finalization period has not elapsed");
   690          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   691  
   692          // Ensure that bob's balance has remained the same
   693          assertEq(bobBalanceBefore, address(bob).balance);
   694      }
   695  
   696      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the target reverts.
   697      function test_finalizeWithdrawalTransaction_targetFails_fails() external {
   698          uint256 bobBalanceBefore = address(bob).balance;
   699          vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
   700  
   701          vm.expectEmit(true, true, true, true);
   702          emit WithdrawalProven(_withdrawalHash, alice, bob);
   703          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   704  
   705          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   706          vm.expectEmit(true, true, true, true);
   707          emit WithdrawalFinalized(_withdrawalHash, false);
   708          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   709  
   710          assert(address(bob).balance == bobBalanceBefore);
   711      }
   712  
   713      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the finalization period
   714      ///      has not yet passed.
   715      function test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() external {
   716          // Setup the Oracle to return an output with a recent timestamp
   717          uint256 recentTimestamp = block.timestamp - 1;
   718          vm.mockCall(
   719              address(optimismPortal.l2Oracle()),
   720              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   721              abi.encode(Types.OutputProposal(_outputRoot, uint128(recentTimestamp), uint128(_proposedBlockNumber)))
   722          );
   723  
   724          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   725  
   726          vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed");
   727          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   728      }
   729  
   730      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal has already been
   731      ///      finalized.
   732      function test_finalizeWithdrawalTransaction_onReplay_reverts() external {
   733          vm.expectEmit(true, true, true, true);
   734          emit WithdrawalProven(_withdrawalHash, alice, bob);
   735          optimismPortal.proveWithdrawalTransaction(_defaultTx, _proposedOutputIndex, _outputRootProof, _withdrawalProof);
   736  
   737          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   738          vm.expectEmit(true, true, true, true);
   739          emit WithdrawalFinalized(_withdrawalHash, true);
   740          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   741  
   742          vm.expectRevert("OptimismPortal: withdrawal has already been finalized");
   743          optimismPortal.finalizeWithdrawalTransaction(_defaultTx);
   744      }
   745  
   746      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if the withdrawal transaction
   747      ///      does not have enough gas to execute.
   748      function test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() external {
   749          // This number was identified through trial and error.
   750          uint256 gasLimit = 150_000;
   751          Types.WithdrawalTransaction memory insufficientGasTx = Types.WithdrawalTransaction({
   752              nonce: 0,
   753              sender: alice,
   754              target: bob,
   755              value: 100,
   756              gasLimit: gasLimit,
   757              data: hex""
   758          });
   759  
   760          // Get updated proof inputs.
   761          (bytes32 stateRoot, bytes32 storageRoot,,, bytes[] memory withdrawalProof) =
   762              ffi.getProveWithdrawalTransactionInputs(insufficientGasTx);
   763          Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
   764              version: bytes32(0),
   765              stateRoot: stateRoot,
   766              messagePasserStorageRoot: storageRoot,
   767              latestBlockhash: bytes32(0)
   768          });
   769  
   770          vm.mockCall(
   771              address(optimismPortal.l2Oracle()),
   772              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   773              abi.encode(
   774                  Types.OutputProposal(
   775                      Hashing.hashOutputRootProof(outputRootProof),
   776                      uint128(block.timestamp),
   777                      uint128(_proposedBlockNumber)
   778                  )
   779              )
   780          );
   781  
   782          optimismPortal.proveWithdrawalTransaction(
   783              insufficientGasTx, _proposedOutputIndex, outputRootProof, withdrawalProof
   784          );
   785  
   786          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   787          vm.expectRevert("SafeCall: Not enough gas");
   788          optimismPortal.finalizeWithdrawalTransaction{ gas: gasLimit }(insufficientGasTx);
   789      }
   790  
   791      /// @dev Tests that `finalizeWithdrawalTransaction` reverts if a sub-call attempts to finalize
   792      ///      another withdrawal.
   793      function test_finalizeWithdrawalTransaction_onReentrancy_reverts() external {
   794          uint256 bobBalanceBefore = address(bob).balance;
   795  
   796          // Copy and modify the default test values to attempt a reentrant call by first calling to
   797          // this contract's callPortalAndExpectRevert() function above.
   798          Types.WithdrawalTransaction memory _testTx = _defaultTx;
   799          _testTx.target = address(this);
   800          _testTx.data = abi.encodeWithSelector(this.callPortalAndExpectRevert.selector);
   801  
   802          // Get modified proof inputs.
   803          (
   804              bytes32 stateRoot,
   805              bytes32 storageRoot,
   806              bytes32 outputRoot,
   807              bytes32 withdrawalHash,
   808              bytes[] memory withdrawalProof
   809          ) = ffi.getProveWithdrawalTransactionInputs(_testTx);
   810          Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
   811              version: bytes32(0),
   812              stateRoot: stateRoot,
   813              messagePasserStorageRoot: storageRoot,
   814              latestBlockhash: bytes32(0)
   815          });
   816  
   817          // Setup the Oracle to return the outputRoot we want as well as a finalized timestamp.
   818          uint256 finalizedTimestamp = block.timestamp - l2OutputOracle.FINALIZATION_PERIOD_SECONDS() - 1;
   819          vm.mockCall(
   820              address(optimismPortal.l2Oracle()),
   821              abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
   822              abi.encode(Types.OutputProposal(outputRoot, uint128(finalizedTimestamp), uint128(_proposedBlockNumber)))
   823          );
   824  
   825          vm.expectEmit(true, true, true, true);
   826          emit WithdrawalProven(withdrawalHash, alice, address(this));
   827          optimismPortal.proveWithdrawalTransaction(_testTx, _proposedBlockNumber, outputRootProof, withdrawalProof);
   828  
   829          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   830          vm.expectCall(address(this), _testTx.data);
   831          vm.expectEmit(true, true, true, true);
   832          emit WithdrawalFinalized(withdrawalHash, true);
   833          optimismPortal.finalizeWithdrawalTransaction(_testTx);
   834  
   835          // Ensure that bob's balance was not changed by the reentrant call.
   836          assert(address(bob).balance == bobBalanceBefore);
   837      }
   838  
   839      /// @dev Tests that `finalizeWithdrawalTransaction` succeeds.
   840      function testDiff_finalizeWithdrawalTransaction_succeeds(
   841          address _sender,
   842          address _target,
   843          uint256 _value,
   844          uint256 _gasLimit,
   845          bytes memory _data
   846      )
   847          external
   848      {
   849          vm.assume(
   850              _target != address(optimismPortal) // Cannot call the optimism portal or a contract
   851                  && _target.code.length == 0 // No accounts with code
   852                  && _target != CONSOLE // The console has no code but behaves like a contract
   853                  && uint160(_target) > 9 // No precompiles (or zero address)
   854          );
   855  
   856          // Total ETH supply is currently about 120M ETH.
   857          uint256 value = bound(_value, 0, 200_000_000 ether);
   858          vm.deal(address(optimismPortal), value);
   859  
   860          uint256 gasLimit = bound(_gasLimit, 0, 50_000_000);
   861          uint256 nonce = l2ToL1MessagePasser.messageNonce();
   862  
   863          // Get a withdrawal transaction and mock proof from the differential testing script.
   864          Types.WithdrawalTransaction memory _tx = Types.WithdrawalTransaction({
   865              nonce: nonce,
   866              sender: _sender,
   867              target: _target,
   868              value: value,
   869              gasLimit: gasLimit,
   870              data: _data
   871          });
   872          (
   873              bytes32 stateRoot,
   874              bytes32 storageRoot,
   875              bytes32 outputRoot,
   876              bytes32 withdrawalHash,
   877              bytes[] memory withdrawalProof
   878          ) = ffi.getProveWithdrawalTransactionInputs(_tx);
   879  
   880          // Create the output root proof
   881          Types.OutputRootProof memory proof = Types.OutputRootProof({
   882              version: bytes32(uint256(0)),
   883              stateRoot: stateRoot,
   884              messagePasserStorageRoot: storageRoot,
   885              latestBlockhash: bytes32(uint256(0))
   886          });
   887  
   888          // Ensure the values returned from ffi are correct
   889          assertEq(outputRoot, Hashing.hashOutputRootProof(proof));
   890          assertEq(withdrawalHash, Hashing.hashWithdrawal(_tx));
   891  
   892          // Setup the Oracle to return the outputRoot
   893          vm.mockCall(
   894              address(l2OutputOracle),
   895              abi.encodeWithSelector(l2OutputOracle.getL2Output.selector),
   896              abi.encode(outputRoot, block.timestamp, 100)
   897          );
   898  
   899          // Prove the withdrawal transaction
   900          optimismPortal.proveWithdrawalTransaction(
   901              _tx,
   902              100, // l2BlockNumber
   903              proof,
   904              withdrawalProof
   905          );
   906          (bytes32 _root,,) = optimismPortal.provenWithdrawals(withdrawalHash);
   907          assertTrue(_root != bytes32(0));
   908  
   909          // Warp past the finalization period
   910          vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
   911  
   912          // Finalize the withdrawal transaction
   913          vm.expectCallMinGas(_tx.target, _tx.value, uint64(_tx.gasLimit), _tx.data);
   914          optimismPortal.finalizeWithdrawalTransaction(_tx);
   915          assertTrue(optimismPortal.finalizedWithdrawals(withdrawalHash));
   916      }
   917  }
   918  
   919  contract OptimismPortalUpgradeable_Test is CommonTest {
   920      /// @dev Tests that the proxy is initialized correctly.
   921      function test_params_initValuesOnProxy_succeeds() external {
   922          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal.params();
   923          ResourceMetering.ResourceConfig memory rcfg = systemConfig.resourceConfig();
   924  
   925          assertEq(prevBaseFee, rcfg.minimumBaseFee);
   926          assertEq(prevBoughtGas, 0);
   927          assertEq(prevBlockNum, block.number);
   928      }
   929  
   930      /// @dev Tests that the proxy can be upgraded.
   931      function test_upgradeToAndCall_upgrading_succeeds() external {
   932          // Check an unused slot before upgrading.
   933          bytes32 slot21Before = vm.load(address(optimismPortal), bytes32(uint256(21)));
   934          assertEq(bytes32(0), slot21Before);
   935  
   936          NextImpl nextImpl = new NextImpl();
   937  
   938          vm.startPrank(EIP1967Helper.getAdmin(address(optimismPortal)));
   939          // The value passed to the initialize must be larger than the last value
   940          // that initialize was called with.
   941          Proxy(payable(address(optimismPortal))).upgradeToAndCall(
   942              address(nextImpl), abi.encodeWithSelector(NextImpl.initialize.selector, 2)
   943          );
   944          assertEq(Proxy(payable(address(optimismPortal))).implementation(), address(nextImpl));
   945  
   946          // Verify that the NextImpl contract initialized its values according as expected
   947          bytes32 slot21After = vm.load(address(optimismPortal), bytes32(uint256(21)));
   948          bytes32 slot21Expected = NextImpl(address(optimismPortal)).slot21Init();
   949          assertEq(slot21Expected, slot21After);
   950      }
   951  }
   952  
   953  /// @title OptimismPortalResourceFuzz_Test
   954  /// @dev Test various values of the resource metering config to ensure that deposits cannot be
   955  ///      broken by changing the config.
   956  contract OptimismPortalResourceFuzz_Test is CommonTest {
   957      /// @dev The max gas limit observed throughout this test. Setting this too high can cause
   958      ///      the test to take too long to run.
   959      uint256 constant MAX_GAS_LIMIT = 30_000_000;
   960  
   961      /// @dev Test that various values of the resource metering config will not break deposits.
   962      function testFuzz_systemConfigDeposit_succeeds(
   963          uint32 _maxResourceLimit,
   964          uint8 _elasticityMultiplier,
   965          uint8 _baseFeeMaxChangeDenominator,
   966          uint32 _minimumBaseFee,
   967          uint32 _systemTxMaxGas,
   968          uint128 _maximumBaseFee,
   969          uint64 _gasLimit,
   970          uint64 _prevBoughtGas,
   971          uint128 _prevBaseFee,
   972          uint8 _blockDiff
   973      )
   974          external
   975      {
   976          // Get the set system gas limit
   977          uint64 gasLimit = systemConfig.gasLimit();
   978          // Bound resource config
   979          _maxResourceLimit = uint32(bound(_maxResourceLimit, 21000, MAX_GAS_LIMIT / 8));
   980          _gasLimit = uint64(bound(_gasLimit, 21000, _maxResourceLimit));
   981          _prevBaseFee = uint128(bound(_prevBaseFee, 0, 3 gwei));
   982          // Prevent values that would cause reverts
   983          vm.assume(gasLimit >= _gasLimit);
   984          vm.assume(_minimumBaseFee < _maximumBaseFee);
   985          vm.assume(_baseFeeMaxChangeDenominator > 1);
   986          vm.assume(uint256(_maxResourceLimit) + uint256(_systemTxMaxGas) <= gasLimit);
   987          vm.assume(_elasticityMultiplier > 0);
   988          vm.assume(((_maxResourceLimit / _elasticityMultiplier) * _elasticityMultiplier) == _maxResourceLimit);
   989          _prevBoughtGas = uint64(bound(_prevBoughtGas, 0, _maxResourceLimit - _gasLimit));
   990          _blockDiff = uint8(bound(_blockDiff, 0, 3));
   991          // Pick a pseudorandom block number
   992          vm.roll(uint256(keccak256(abi.encode(_blockDiff))) % uint256(type(uint16).max) + uint256(_blockDiff));
   993  
   994          // Create a resource config to mock the call to the system config with
   995          ResourceMetering.ResourceConfig memory rcfg = ResourceMetering.ResourceConfig({
   996              maxResourceLimit: _maxResourceLimit,
   997              elasticityMultiplier: _elasticityMultiplier,
   998              baseFeeMaxChangeDenominator: _baseFeeMaxChangeDenominator,
   999              minimumBaseFee: _minimumBaseFee,
  1000              systemTxMaxGas: _systemTxMaxGas,
  1001              maximumBaseFee: _maximumBaseFee
  1002          });
  1003          vm.mockCall(
  1004              address(systemConfig), abi.encodeWithSelector(systemConfig.resourceConfig.selector), abi.encode(rcfg)
  1005          );
  1006  
  1007          // Set the resource params
  1008          uint256 _prevBlockNum = block.number - _blockDiff;
  1009          vm.store(
  1010              address(optimismPortal),
  1011              bytes32(uint256(1)),
  1012              bytes32((_prevBlockNum << 192) | (uint256(_prevBoughtGas) << 128) | _prevBaseFee)
  1013          );
  1014          // Ensure that the storage setting is correct
  1015          (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = optimismPortal.params();
  1016          assertEq(prevBaseFee, _prevBaseFee);
  1017          assertEq(prevBoughtGas, _prevBoughtGas);
  1018          assertEq(prevBlockNum, _prevBlockNum);
  1019  
  1020          // Do a deposit, should not revert
  1021          optimismPortal.depositTransaction{ gas: MAX_GAS_LIMIT }({
  1022              _to: address(0x20),
  1023              _value: 0x40,
  1024              _gasLimit: _gasLimit,
  1025              _isCreation: false,
  1026              _data: hex""
  1027          });
  1028      }
  1029  }