ETH Price: $4,340.35 (+2.50%)

Transaction Decoder

Block:
22714922 at Jun-16-2025 05:12:35 AM +UTC
Transaction Fee:
0.00003704875 ETH $0.16
Gas Used:
148,195 Gas / 0.25 Gwei

Account State Difference:

  Address   Before After State Difference Code
(MEV Builder: 0x6adb...200)
5.012775134533227358 Eth5.012777607504242373 Eth0.000002472971015015
0xaD7D50D8...3997E6E54
0.312883978874883412 Eth
Nonce: 165
0.312846930124883412 Eth
Nonce: 166
0.00003704875

Execution Trace

ETH 0.050547426983306861 DiamondProxy.eb672419( )
  • ETH 0.050547426983306861 MailboxFacet.requestL2Transaction( _contractL2=0xaD7D50D88DCfeC80D97892469AD19B23997E6E54, _l2Value=50000000000000000, _calldata=0x, _l2GasLimit=742563, _l2GasPerPubdataByteLimit=800, _factoryDeps=[], _refundRecipient=0xaD7D50D88DCfeC80D97892469AD19B23997E6E54 )
    File 1 of 2: DiamondProxy
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
    // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
     * checks.
     *
     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
     * easily result in undesired exploitation or bugs, since developers usually
     * assume that overflows raise errors. `SafeCast` restores this intuition by
     * reverting the transaction when such an operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     *
     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
     * all math on `uint256` and `int256` and then downcasting.
     */
    library SafeCast {
        /**
         * @dev Returns the downcasted uint248 from uint256, reverting on
         * overflow (when the input is greater than largest uint248).
         *
         * Counterpart to Solidity's `uint248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         *
         * _Available since v4.7._
         */
        function toUint248(uint256 value) internal pure returns (uint248) {
            require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
            return uint248(value);
        }
        /**
         * @dev Returns the downcasted uint240 from uint256, reverting on
         * overflow (when the input is greater than largest uint240).
         *
         * Counterpart to Solidity's `uint240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         *
         * _Available since v4.7._
         */
        function toUint240(uint256 value) internal pure returns (uint240) {
            require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
            return uint240(value);
        }
        /**
         * @dev Returns the downcasted uint232 from uint256, reverting on
         * overflow (when the input is greater than largest uint232).
         *
         * Counterpart to Solidity's `uint232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         *
         * _Available since v4.7._
         */
        function toUint232(uint256 value) internal pure returns (uint232) {
            require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
            return uint232(value);
        }
        /**
         * @dev Returns the downcasted uint224 from uint256, reverting on
         * overflow (when the input is greater than largest uint224).
         *
         * Counterpart to Solidity's `uint224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         *
         * _Available since v4.2._
         */
        function toUint224(uint256 value) internal pure returns (uint224) {
            require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
            return uint224(value);
        }
        /**
         * @dev Returns the downcasted uint216 from uint256, reverting on
         * overflow (when the input is greater than largest uint216).
         *
         * Counterpart to Solidity's `uint216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         *
         * _Available since v4.7._
         */
        function toUint216(uint256 value) internal pure returns (uint216) {
            require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
            return uint216(value);
        }
        /**
         * @dev Returns the downcasted uint208 from uint256, reverting on
         * overflow (when the input is greater than largest uint208).
         *
         * Counterpart to Solidity's `uint208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         *
         * _Available since v4.7._
         */
        function toUint208(uint256 value) internal pure returns (uint208) {
            require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
            return uint208(value);
        }
        /**
         * @dev Returns the downcasted uint200 from uint256, reverting on
         * overflow (when the input is greater than largest uint200).
         *
         * Counterpart to Solidity's `uint200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         *
         * _Available since v4.7._
         */
        function toUint200(uint256 value) internal pure returns (uint200) {
            require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
            return uint200(value);
        }
        /**
         * @dev Returns the downcasted uint192 from uint256, reverting on
         * overflow (when the input is greater than largest uint192).
         *
         * Counterpart to Solidity's `uint192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         *
         * _Available since v4.7._
         */
        function toUint192(uint256 value) internal pure returns (uint192) {
            require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
            return uint192(value);
        }
        /**
         * @dev Returns the downcasted uint184 from uint256, reverting on
         * overflow (when the input is greater than largest uint184).
         *
         * Counterpart to Solidity's `uint184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         *
         * _Available since v4.7._
         */
        function toUint184(uint256 value) internal pure returns (uint184) {
            require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
            return uint184(value);
        }
        /**
         * @dev Returns the downcasted uint176 from uint256, reverting on
         * overflow (when the input is greater than largest uint176).
         *
         * Counterpart to Solidity's `uint176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         *
         * _Available since v4.7._
         */
        function toUint176(uint256 value) internal pure returns (uint176) {
            require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
            return uint176(value);
        }
        /**
         * @dev Returns the downcasted uint168 from uint256, reverting on
         * overflow (when the input is greater than largest uint168).
         *
         * Counterpart to Solidity's `uint168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         *
         * _Available since v4.7._
         */
        function toUint168(uint256 value) internal pure returns (uint168) {
            require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
            return uint168(value);
        }
        /**
         * @dev Returns the downcasted uint160 from uint256, reverting on
         * overflow (when the input is greater than largest uint160).
         *
         * Counterpart to Solidity's `uint160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         *
         * _Available since v4.7._
         */
        function toUint160(uint256 value) internal pure returns (uint160) {
            require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
            return uint160(value);
        }
        /**
         * @dev Returns the downcasted uint152 from uint256, reverting on
         * overflow (when the input is greater than largest uint152).
         *
         * Counterpart to Solidity's `uint152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         *
         * _Available since v4.7._
         */
        function toUint152(uint256 value) internal pure returns (uint152) {
            require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
            return uint152(value);
        }
        /**
         * @dev Returns the downcasted uint144 from uint256, reverting on
         * overflow (when the input is greater than largest uint144).
         *
         * Counterpart to Solidity's `uint144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         *
         * _Available since v4.7._
         */
        function toUint144(uint256 value) internal pure returns (uint144) {
            require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
            return uint144(value);
        }
        /**
         * @dev Returns the downcasted uint136 from uint256, reverting on
         * overflow (when the input is greater than largest uint136).
         *
         * Counterpart to Solidity's `uint136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         *
         * _Available since v4.7._
         */
        function toUint136(uint256 value) internal pure returns (uint136) {
            require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
            return uint136(value);
        }
        /**
         * @dev Returns the downcasted uint128 from uint256, reverting on
         * overflow (when the input is greater than largest uint128).
         *
         * Counterpart to Solidity's `uint128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         *
         * _Available since v2.5._
         */
        function toUint128(uint256 value) internal pure returns (uint128) {
            require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
            return uint128(value);
        }
        /**
         * @dev Returns the downcasted uint120 from uint256, reverting on
         * overflow (when the input is greater than largest uint120).
         *
         * Counterpart to Solidity's `uint120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         *
         * _Available since v4.7._
         */
        function toUint120(uint256 value) internal pure returns (uint120) {
            require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
            return uint120(value);
        }
        /**
         * @dev Returns the downcasted uint112 from uint256, reverting on
         * overflow (when the input is greater than largest uint112).
         *
         * Counterpart to Solidity's `uint112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         *
         * _Available since v4.7._
         */
        function toUint112(uint256 value) internal pure returns (uint112) {
            require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
            return uint112(value);
        }
        /**
         * @dev Returns the downcasted uint104 from uint256, reverting on
         * overflow (when the input is greater than largest uint104).
         *
         * Counterpart to Solidity's `uint104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         *
         * _Available since v4.7._
         */
        function toUint104(uint256 value) internal pure returns (uint104) {
            require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
            return uint104(value);
        }
        /**
         * @dev Returns the downcasted uint96 from uint256, reverting on
         * overflow (when the input is greater than largest uint96).
         *
         * Counterpart to Solidity's `uint96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         *
         * _Available since v4.2._
         */
        function toUint96(uint256 value) internal pure returns (uint96) {
            require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
            return uint96(value);
        }
        /**
         * @dev Returns the downcasted uint88 from uint256, reverting on
         * overflow (when the input is greater than largest uint88).
         *
         * Counterpart to Solidity's `uint88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         *
         * _Available since v4.7._
         */
        function toUint88(uint256 value) internal pure returns (uint88) {
            require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
            return uint88(value);
        }
        /**
         * @dev Returns the downcasted uint80 from uint256, reverting on
         * overflow (when the input is greater than largest uint80).
         *
         * Counterpart to Solidity's `uint80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         *
         * _Available since v4.7._
         */
        function toUint80(uint256 value) internal pure returns (uint80) {
            require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
            return uint80(value);
        }
        /**
         * @dev Returns the downcasted uint72 from uint256, reverting on
         * overflow (when the input is greater than largest uint72).
         *
         * Counterpart to Solidity's `uint72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         *
         * _Available since v4.7._
         */
        function toUint72(uint256 value) internal pure returns (uint72) {
            require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
            return uint72(value);
        }
        /**
         * @dev Returns the downcasted uint64 from uint256, reverting on
         * overflow (when the input is greater than largest uint64).
         *
         * Counterpart to Solidity's `uint64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         *
         * _Available since v2.5._
         */
        function toUint64(uint256 value) internal pure returns (uint64) {
            require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
            return uint64(value);
        }
        /**
         * @dev Returns the downcasted uint56 from uint256, reverting on
         * overflow (when the input is greater than largest uint56).
         *
         * Counterpart to Solidity's `uint56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         *
         * _Available since v4.7._
         */
        function toUint56(uint256 value) internal pure returns (uint56) {
            require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
            return uint56(value);
        }
        /**
         * @dev Returns the downcasted uint48 from uint256, reverting on
         * overflow (when the input is greater than largest uint48).
         *
         * Counterpart to Solidity's `uint48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         *
         * _Available since v4.7._
         */
        function toUint48(uint256 value) internal pure returns (uint48) {
            require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
            return uint48(value);
        }
        /**
         * @dev Returns the downcasted uint40 from uint256, reverting on
         * overflow (when the input is greater than largest uint40).
         *
         * Counterpart to Solidity's `uint40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         *
         * _Available since v4.7._
         */
        function toUint40(uint256 value) internal pure returns (uint40) {
            require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
            return uint40(value);
        }
        /**
         * @dev Returns the downcasted uint32 from uint256, reverting on
         * overflow (when the input is greater than largest uint32).
         *
         * Counterpart to Solidity's `uint32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         *
         * _Available since v2.5._
         */
        function toUint32(uint256 value) internal pure returns (uint32) {
            require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
            return uint32(value);
        }
        /**
         * @dev Returns the downcasted uint24 from uint256, reverting on
         * overflow (when the input is greater than largest uint24).
         *
         * Counterpart to Solidity's `uint24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         *
         * _Available since v4.7._
         */
        function toUint24(uint256 value) internal pure returns (uint24) {
            require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
            return uint24(value);
        }
        /**
         * @dev Returns the downcasted uint16 from uint256, reverting on
         * overflow (when the input is greater than largest uint16).
         *
         * Counterpart to Solidity's `uint16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         *
         * _Available since v2.5._
         */
        function toUint16(uint256 value) internal pure returns (uint16) {
            require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
            return uint16(value);
        }
        /**
         * @dev Returns the downcasted uint8 from uint256, reverting on
         * overflow (when the input is greater than largest uint8).
         *
         * Counterpart to Solidity's `uint8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         *
         * _Available since v2.5._
         */
        function toUint8(uint256 value) internal pure returns (uint8) {
            require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
            return uint8(value);
        }
        /**
         * @dev Converts a signed int256 into an unsigned uint256.
         *
         * Requirements:
         *
         * - input must be greater than or equal to 0.
         *
         * _Available since v3.0._
         */
        function toUint256(int256 value) internal pure returns (uint256) {
            require(value >= 0, "SafeCast: value must be positive");
            return uint256(value);
        }
        /**
         * @dev Returns the downcasted int248 from int256, reverting on
         * overflow (when the input is less than smallest int248 or
         * greater than largest int248).
         *
         * Counterpart to Solidity's `int248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         *
         * _Available since v4.7._
         */
        function toInt248(int256 value) internal pure returns (int248 downcasted) {
            downcasted = int248(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
        }
        /**
         * @dev Returns the downcasted int240 from int256, reverting on
         * overflow (when the input is less than smallest int240 or
         * greater than largest int240).
         *
         * Counterpart to Solidity's `int240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         *
         * _Available since v4.7._
         */
        function toInt240(int256 value) internal pure returns (int240 downcasted) {
            downcasted = int240(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
        }
        /**
         * @dev Returns the downcasted int232 from int256, reverting on
         * overflow (when the input is less than smallest int232 or
         * greater than largest int232).
         *
         * Counterpart to Solidity's `int232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         *
         * _Available since v4.7._
         */
        function toInt232(int256 value) internal pure returns (int232 downcasted) {
            downcasted = int232(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
        }
        /**
         * @dev Returns the downcasted int224 from int256, reverting on
         * overflow (when the input is less than smallest int224 or
         * greater than largest int224).
         *
         * Counterpart to Solidity's `int224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         *
         * _Available since v4.7._
         */
        function toInt224(int256 value) internal pure returns (int224 downcasted) {
            downcasted = int224(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
        }
        /**
         * @dev Returns the downcasted int216 from int256, reverting on
         * overflow (when the input is less than smallest int216 or
         * greater than largest int216).
         *
         * Counterpart to Solidity's `int216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         *
         * _Available since v4.7._
         */
        function toInt216(int256 value) internal pure returns (int216 downcasted) {
            downcasted = int216(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
        }
        /**
         * @dev Returns the downcasted int208 from int256, reverting on
         * overflow (when the input is less than smallest int208 or
         * greater than largest int208).
         *
         * Counterpart to Solidity's `int208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         *
         * _Available since v4.7._
         */
        function toInt208(int256 value) internal pure returns (int208 downcasted) {
            downcasted = int208(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
        }
        /**
         * @dev Returns the downcasted int200 from int256, reverting on
         * overflow (when the input is less than smallest int200 or
         * greater than largest int200).
         *
         * Counterpart to Solidity's `int200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         *
         * _Available since v4.7._
         */
        function toInt200(int256 value) internal pure returns (int200 downcasted) {
            downcasted = int200(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
        }
        /**
         * @dev Returns the downcasted int192 from int256, reverting on
         * overflow (when the input is less than smallest int192 or
         * greater than largest int192).
         *
         * Counterpart to Solidity's `int192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         *
         * _Available since v4.7._
         */
        function toInt192(int256 value) internal pure returns (int192 downcasted) {
            downcasted = int192(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
        }
        /**
         * @dev Returns the downcasted int184 from int256, reverting on
         * overflow (when the input is less than smallest int184 or
         * greater than largest int184).
         *
         * Counterpart to Solidity's `int184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         *
         * _Available since v4.7._
         */
        function toInt184(int256 value) internal pure returns (int184 downcasted) {
            downcasted = int184(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
        }
        /**
         * @dev Returns the downcasted int176 from int256, reverting on
         * overflow (when the input is less than smallest int176 or
         * greater than largest int176).
         *
         * Counterpart to Solidity's `int176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         *
         * _Available since v4.7._
         */
        function toInt176(int256 value) internal pure returns (int176 downcasted) {
            downcasted = int176(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
        }
        /**
         * @dev Returns the downcasted int168 from int256, reverting on
         * overflow (when the input is less than smallest int168 or
         * greater than largest int168).
         *
         * Counterpart to Solidity's `int168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         *
         * _Available since v4.7._
         */
        function toInt168(int256 value) internal pure returns (int168 downcasted) {
            downcasted = int168(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
        }
        /**
         * @dev Returns the downcasted int160 from int256, reverting on
         * overflow (when the input is less than smallest int160 or
         * greater than largest int160).
         *
         * Counterpart to Solidity's `int160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         *
         * _Available since v4.7._
         */
        function toInt160(int256 value) internal pure returns (int160 downcasted) {
            downcasted = int160(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
        }
        /**
         * @dev Returns the downcasted int152 from int256, reverting on
         * overflow (when the input is less than smallest int152 or
         * greater than largest int152).
         *
         * Counterpart to Solidity's `int152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         *
         * _Available since v4.7._
         */
        function toInt152(int256 value) internal pure returns (int152 downcasted) {
            downcasted = int152(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
        }
        /**
         * @dev Returns the downcasted int144 from int256, reverting on
         * overflow (when the input is less than smallest int144 or
         * greater than largest int144).
         *
         * Counterpart to Solidity's `int144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         *
         * _Available since v4.7._
         */
        function toInt144(int256 value) internal pure returns (int144 downcasted) {
            downcasted = int144(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
        }
        /**
         * @dev Returns the downcasted int136 from int256, reverting on
         * overflow (when the input is less than smallest int136 or
         * greater than largest int136).
         *
         * Counterpart to Solidity's `int136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         *
         * _Available since v4.7._
         */
        function toInt136(int256 value) internal pure returns (int136 downcasted) {
            downcasted = int136(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
        }
        /**
         * @dev Returns the downcasted int128 from int256, reverting on
         * overflow (when the input is less than smallest int128 or
         * greater than largest int128).
         *
         * Counterpart to Solidity's `int128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         *
         * _Available since v3.1._
         */
        function toInt128(int256 value) internal pure returns (int128 downcasted) {
            downcasted = int128(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
        }
        /**
         * @dev Returns the downcasted int120 from int256, reverting on
         * overflow (when the input is less than smallest int120 or
         * greater than largest int120).
         *
         * Counterpart to Solidity's `int120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         *
         * _Available since v4.7._
         */
        function toInt120(int256 value) internal pure returns (int120 downcasted) {
            downcasted = int120(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
        }
        /**
         * @dev Returns the downcasted int112 from int256, reverting on
         * overflow (when the input is less than smallest int112 or
         * greater than largest int112).
         *
         * Counterpart to Solidity's `int112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         *
         * _Available since v4.7._
         */
        function toInt112(int256 value) internal pure returns (int112 downcasted) {
            downcasted = int112(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
        }
        /**
         * @dev Returns the downcasted int104 from int256, reverting on
         * overflow (when the input is less than smallest int104 or
         * greater than largest int104).
         *
         * Counterpart to Solidity's `int104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         *
         * _Available since v4.7._
         */
        function toInt104(int256 value) internal pure returns (int104 downcasted) {
            downcasted = int104(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
        }
        /**
         * @dev Returns the downcasted int96 from int256, reverting on
         * overflow (when the input is less than smallest int96 or
         * greater than largest int96).
         *
         * Counterpart to Solidity's `int96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         *
         * _Available since v4.7._
         */
        function toInt96(int256 value) internal pure returns (int96 downcasted) {
            downcasted = int96(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
        }
        /**
         * @dev Returns the downcasted int88 from int256, reverting on
         * overflow (when the input is less than smallest int88 or
         * greater than largest int88).
         *
         * Counterpart to Solidity's `int88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         *
         * _Available since v4.7._
         */
        function toInt88(int256 value) internal pure returns (int88 downcasted) {
            downcasted = int88(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
        }
        /**
         * @dev Returns the downcasted int80 from int256, reverting on
         * overflow (when the input is less than smallest int80 or
         * greater than largest int80).
         *
         * Counterpart to Solidity's `int80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         *
         * _Available since v4.7._
         */
        function toInt80(int256 value) internal pure returns (int80 downcasted) {
            downcasted = int80(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
        }
        /**
         * @dev Returns the downcasted int72 from int256, reverting on
         * overflow (when the input is less than smallest int72 or
         * greater than largest int72).
         *
         * Counterpart to Solidity's `int72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         *
         * _Available since v4.7._
         */
        function toInt72(int256 value) internal pure returns (int72 downcasted) {
            downcasted = int72(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
        }
        /**
         * @dev Returns the downcasted int64 from int256, reverting on
         * overflow (when the input is less than smallest int64 or
         * greater than largest int64).
         *
         * Counterpart to Solidity's `int64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         *
         * _Available since v3.1._
         */
        function toInt64(int256 value) internal pure returns (int64 downcasted) {
            downcasted = int64(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
        }
        /**
         * @dev Returns the downcasted int56 from int256, reverting on
         * overflow (when the input is less than smallest int56 or
         * greater than largest int56).
         *
         * Counterpart to Solidity's `int56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         *
         * _Available since v4.7._
         */
        function toInt56(int256 value) internal pure returns (int56 downcasted) {
            downcasted = int56(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
        }
        /**
         * @dev Returns the downcasted int48 from int256, reverting on
         * overflow (when the input is less than smallest int48 or
         * greater than largest int48).
         *
         * Counterpart to Solidity's `int48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         *
         * _Available since v4.7._
         */
        function toInt48(int256 value) internal pure returns (int48 downcasted) {
            downcasted = int48(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
        }
        /**
         * @dev Returns the downcasted int40 from int256, reverting on
         * overflow (when the input is less than smallest int40 or
         * greater than largest int40).
         *
         * Counterpart to Solidity's `int40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         *
         * _Available since v4.7._
         */
        function toInt40(int256 value) internal pure returns (int40 downcasted) {
            downcasted = int40(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
        }
        /**
         * @dev Returns the downcasted int32 from int256, reverting on
         * overflow (when the input is less than smallest int32 or
         * greater than largest int32).
         *
         * Counterpart to Solidity's `int32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         *
         * _Available since v3.1._
         */
        function toInt32(int256 value) internal pure returns (int32 downcasted) {
            downcasted = int32(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
        }
        /**
         * @dev Returns the downcasted int24 from int256, reverting on
         * overflow (when the input is less than smallest int24 or
         * greater than largest int24).
         *
         * Counterpart to Solidity's `int24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         *
         * _Available since v4.7._
         */
        function toInt24(int256 value) internal pure returns (int24 downcasted) {
            downcasted = int24(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
        }
        /**
         * @dev Returns the downcasted int16 from int256, reverting on
         * overflow (when the input is less than smallest int16 or
         * greater than largest int16).
         *
         * Counterpart to Solidity's `int16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         *
         * _Available since v3.1._
         */
        function toInt16(int256 value) internal pure returns (int16 downcasted) {
            downcasted = int16(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
        }
        /**
         * @dev Returns the downcasted int8 from int256, reverting on
         * overflow (when the input is less than smallest int8 or
         * greater than largest int8).
         *
         * Counterpart to Solidity's `int8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         *
         * _Available since v3.1._
         */
        function toInt8(int256 value) internal pure returns (int8 downcasted) {
            downcasted = int8(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
        }
        /**
         * @dev Converts an unsigned uint256 into a signed int256.
         *
         * Requirements:
         *
         * - input must be less than or equal to maxInt256.
         *
         * _Available since v3.0._
         */
        function toInt256(uint256 value) internal pure returns (int256) {
            // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
            require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
            return int256(value);
        }
    }
    pragma solidity ^0.8.0;
    // SPDX-License-Identifier: MIT
    library UncheckedMath {
        function uncheckedInc(uint256 _number) internal pure returns (uint256) {
            unchecked {
                return _number + 1;
            }
        }
        function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
            unchecked {
                return _lhs + _rhs;
            }
        }
    }
    pragma solidity ^0.8.0;
    // SPDX-License-Identifier: MIT
    import "./libraries/Diamond.sol";
    /// @title Diamond Proxy Contract (EIP-2535)
    /// @author Matter Labs
    contract DiamondProxy {
        constructor(uint256 _chainId, Diamond.DiamondCutData memory _diamondCut) {
            // Check that the contract is deployed on the expected chain.
            // Thus, the contract deployed by the same Create2 factory on the different chain will have different addresses!
            require(_chainId == block.chainid, "pr");
            Diamond.diamondCut(_diamondCut);
        }
        /// @dev 1. Find the facet for the function that is called.
        /// @dev 2. Delegate the execution to the found facet via `delegatecall`.
        fallback() external payable {
            Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
            // Check whether the data contains a "full" selector or it is empty.
            // Required because Diamond proxy finds a facet by function signature,
            // which is not defined for data length in range [1, 3].
            require(msg.data.length >= 4 || msg.data.length == 0, "Ut");
            // Get facet from function selector
            Diamond.SelectorToFacet memory facet = diamondStorage.selectorToFacet[msg.sig];
            address facetAddress = facet.facetAddress;
            require(facetAddress != address(0), "F"); // Proxy has no facet for this selector
            require(!diamondStorage.isFrozen || !facet.isFreezable, "q1"); // Facet is frozen
            assembly {
                // The pointer to the free memory slot
                let ptr := mload(0x40)
                // Copy function signature and arguments from calldata at zero position into memory at pointer position
                calldatacopy(ptr, 0, calldatasize())
                // Delegatecall method of the implementation contract returns 0 on error
                let result := delegatecall(gas(), facetAddress, ptr, calldatasize(), 0, 0)
                // Get the size of the last return data
                let size := returndatasize()
                // Copy the size length of bytes from return data at zero position to pointer position
                returndatacopy(ptr, 0, size)
                // Depending on the result value
                switch result
                case 0 {
                    // End execution and revert state changes
                    revert(ptr, size)
                }
                default {
                    // Return data with length of size at pointers position
                    return(ptr, size)
                }
            }
        }
    }
    pragma solidity ^0.8.0;
    // SPDX-License-Identifier: MIT
    import "@openzeppelin/contracts/utils/math/SafeCast.sol";
    import "../../common/libraries/UncheckedMath.sol";
    /// @author Matter Labs
    /// @notice The helper library for managing the EIP-2535 diamond proxy.
    library Diamond {
        using UncheckedMath for uint256;
        using SafeCast for uint256;
        /// @dev Magic value that should be returned by diamond cut initialize contracts.
        /// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts.
        bytes32 constant DIAMOND_INIT_SUCCESS_RETURN_VALUE =
            0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1
        /// @dev Storage position of `DiamondStorage` structure.
        bytes32 constant DIAMOND_STORAGE_POSITION = 0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1;
        event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);
        /// @dev Utility struct that contains associated facet & meta information of selector
        /// @param facetAddress address of the facet which is connected with selector
        /// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored
        /// @param isFreezable denotes whether the selector can be frozen.
        struct SelectorToFacet {
            address facetAddress;
            uint16 selectorPosition;
            bool isFreezable;
        }
        /// @dev Utility struct that contains associated selectors & meta information of facet
        /// @param selectors list of all selectors that belong to the facet
        /// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored
        struct FacetToSelectors {
            bytes4[] selectors;
            uint16 facetPosition;
        }
        /// @notice The structure that holds all diamond proxy associated parameters
        /// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION`
        /// @param selectorToFacet A mapping from the selector to the facet address and its meta information
        /// @param facetToSelectors A mapping from facet address to its selector with meta information
        /// @param facets The array of all unique facet addresses that belong to the diamond proxy
        /// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible
        struct DiamondStorage {
            mapping(bytes4 => SelectorToFacet) selectorToFacet;
            mapping(address => FacetToSelectors) facetToSelectors;
            address[] facets;
            bool isFrozen;
        }
        /// @dev Parameters for diamond changes that touch one of the facets
        /// @param facet The address of facet that's affected by the cut
        /// @param action The action that is made on the facet
        /// @param isFreezable Denotes whether the facet & all their selectors can be frozen
        /// @param selectors An array of unique selectors that belongs to the facet address
        struct FacetCut {
            address facet;
            Action action;
            bool isFreezable;
            bytes4[] selectors;
        }
        /// @dev Structure of the diamond proxy changes
        /// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts
        /// @param initAddress The address that's delegate called after setting up new facet changes
        /// @param initCalldata Calldata for the delegate call to `initAddress`
        struct DiamondCutData {
            FacetCut[] facetCuts;
            address initAddress;
            bytes initCalldata;
        }
        /// @dev Type of change over diamond: add/replace/remove facets
        enum Action {
            Add,
            Replace,
            Remove
        }
        /// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored
        function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) {
            bytes32 position = DIAMOND_STORAGE_POSITION;
            assembly {
                diamondStorage.slot := position
            }
        }
        /// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall
        /// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall
        function diamondCut(DiamondCutData memory _diamondCut) internal {
            FacetCut[] memory facetCuts = _diamondCut.facetCuts;
            address initAddress = _diamondCut.initAddress;
            bytes memory initCalldata = _diamondCut.initCalldata;
            uint256 facetCutsLength = facetCuts.length;
            for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) {
                Action action = facetCuts[i].action;
                address facet = facetCuts[i].facet;
                bool isFacetFreezable = facetCuts[i].isFreezable;
                bytes4[] memory selectors = facetCuts[i].selectors;
                require(selectors.length > 0, "B"); // no functions for diamond cut
                if (action == Action.Add) {
                    _addFunctions(facet, selectors, isFacetFreezable);
                } else if (action == Action.Replace) {
                    _replaceFunctions(facet, selectors, isFacetFreezable);
                } else if (action == Action.Remove) {
                    _removeFunctions(facet, selectors);
                } else {
                    revert("C"); // undefined diamond cut action
                }
            }
            _initializeDiamondCut(initAddress, initCalldata);
            emit DiamondCut(facetCuts, initAddress, initCalldata);
        }
        /// @dev Add new functions to the diamond proxy
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _addFunctions(
            address _facet,
            bytes4[] memory _selectors,
            bool _isFacetFreezable
        ) private {
            DiamondStorage storage ds = getDiamondStorage();
            require(_facet != address(0), "G"); // facet with zero address cannot be added
            // Add facet to the list of facets if the facet address is new one
            _saveFacetIfNew(_facet);
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                require(oldFacet.facetAddress == address(0), "J"); // facet for this selector already exists
                _addOneFunction(_facet, selector, _isFacetFreezable);
            }
        }
        /// @dev Change associated facets to already known function selectors
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _replaceFunctions(
            address _facet,
            bytes4[] memory _selectors,
            bool _isFacetFreezable
        ) private {
            DiamondStorage storage ds = getDiamondStorage();
            require(_facet != address(0), "K"); // cannot replace facet with zero address
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                require(oldFacet.facetAddress != address(0), "L"); // it is impossible to replace the facet with zero address
                _removeOneFunction(oldFacet.facetAddress, selector);
                // Add facet to the list of facets if the facet address is a new one
                _saveFacetIfNew(_facet);
                _addOneFunction(_facet, selector, _isFacetFreezable);
            }
        }
        /// @dev Remove association with function and facet
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _removeFunctions(address _facet, bytes4[] memory _selectors) private {
            DiamondStorage storage ds = getDiamondStorage();
            require(_facet == address(0), "a1"); // facet address must be zero
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                require(oldFacet.facetAddress != address(0), "a2"); // Can't delete a non-existent facet
                _removeOneFunction(oldFacet.facetAddress, selector);
            }
        }
        /// @dev Add address to the list of known facets if it is not on the list yet
        /// NOTE: should be called ONLY before adding a new selector associated with the address
        function _saveFacetIfNew(address _facet) private {
            DiamondStorage storage ds = getDiamondStorage();
            uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length;
            // If there are no selectors associated with facet then save facet as new one
            if (selectorsLength == 0) {
                ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16();
                ds.facets.push(_facet);
            }
        }
        /// @dev Add one function to the already known facet
        /// NOTE: It is expected but NOT enforced that:
        /// - `_facet` is NON-ZERO address
        /// - `_facet` is already stored address in `DiamondStorage.facets`
        /// - `_selector` is NOT associated by another facet
        function _addOneFunction(
            address _facet,
            bytes4 _selector,
            bool _isSelectorFreezable
        ) private {
            DiamondStorage storage ds = getDiamondStorage();
            uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
            // if selectorPosition is nonzero, it means it is not a new facet
            // so the freezability of the first selector must be matched to _isSelectorFreezable
            // so all the selectors in a facet will have the same freezability
            if (selectorPosition != 0) {
                bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0];
                require(_isSelectorFreezable == ds.selectorToFacet[selector0].isFreezable, "J1");
            }
            ds.selectorToFacet[_selector] = SelectorToFacet({
                facetAddress: _facet,
                selectorPosition: selectorPosition,
                isFreezable: _isSelectorFreezable
            });
            ds.facetToSelectors[_facet].selectors.push(_selector);
        }
        /// @dev Remove one associated function with facet
        /// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address
        function _removeOneFunction(address _facet, bytes4 _selector) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Get index of `FacetToSelectors.selectors` of the selector and last element of array
            uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition;
            uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1;
            // If the selector is not at the end of the array then move the last element to the selector position
            if (selectorPosition != lastSelectorPosition) {
                bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition];
                ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector;
                ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16();
            }
            // Remove last element from the selectors array
            ds.facetToSelectors[_facet].selectors.pop();
            // Finally, clean up the association with facet
            delete ds.selectorToFacet[_selector];
            // If there are no selectors for facet then remove the facet from the list of known facets
            if (lastSelectorPosition == 0) {
                _removeFacet(_facet);
            }
        }
        /// @dev remove facet from the list of known facets
        /// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet`
        function _removeFacet(address _facet) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Get index of `DiamondStorage.facets` of the facet and last element of array
            uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition;
            uint256 lastFacetPosition = ds.facets.length - 1;
            // If the facet is not at the end of the array then move the last element to the facet position
            if (facetPosition != lastFacetPosition) {
                address lastFacet = ds.facets[lastFacetPosition];
                ds.facets[facetPosition] = lastFacet;
                ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16();
            }
            // Remove last element from the facets array
            ds.facets.pop();
        }
        /// @dev Delegates call to the initialization address with provided calldata
        /// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets
        function _initializeDiamondCut(address _init, bytes memory _calldata) private {
            if (_init == address(0)) {
                require(_calldata.length == 0, "H"); // Non-empty calldata for zero address
            } else {
                // Do not check whether `_init` is a contract since later we check that it returns data.
                (bool success, bytes memory data) = _init.delegatecall(_calldata);
                require(success, "I"); // delegatecall failed
                // Check that called contract returns magic value to make sure that contract logic
                // supposed to be used as diamond cut initializer.
                require(data.length == 32, "lp");
                require(abi.decode(data, (bytes32)) == DIAMOND_INIT_SUCCESS_RETURN_VALUE, "lp1");
            }
        }
    }
    

    File 2 of 2: MailboxFacet
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {Math} from "@openzeppelin/contracts-v4/utils/math/Math.sol";
    import {IMailbox} from "../../chain-interfaces/IMailbox.sol";
    import {IChainTypeManager} from "../../IChainTypeManager.sol";
    import {IBridgehub} from "../../../bridgehub/IBridgehub.sol";
    import {ITransactionFilterer} from "../../chain-interfaces/ITransactionFilterer.sol";
    import {Merkle} from "../../../common/libraries/Merkle.sol";
    import {PriorityQueue, PriorityOperation} from "../../libraries/PriorityQueue.sol";
    import {PriorityTree} from "../../libraries/PriorityTree.sol";
    import {TransactionValidator} from "../../libraries/TransactionValidator.sol";
    import {WritePriorityOpParams, L2CanonicalTransaction, L2Message, L2Log, TxStatus, BridgehubL2TransactionRequest} from "../../../common/Messaging.sol";
    import {MessageHashing} from "../../../common/libraries/MessageHashing.sol";
    import {FeeParams, PubdataPricingMode} from "../ZKChainStorage.sol";
    import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol";
    import {L2ContractHelper} from "../../../common/libraries/L2ContractHelper.sol";
    import {AddressAliasHelper} from "../../../vendor/AddressAliasHelper.sol";
    import {ZKChainBase} from "./ZKChainBase.sol";
    import {REQUIRED_L2_GAS_PRICE_PER_PUBDATA, L1_GAS_PER_PUBDATA_BYTE, L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH, PRIORITY_OPERATION_L2_TX_TYPE, PRIORITY_EXPIRATION, MAX_NEW_FACTORY_DEPS, SETTLEMENT_LAYER_RELAY_SENDER, SUPPORTED_PROOF_METADATA_VERSION, SERVICE_TRANSACTION_SENDER} from "../../../common/Config.sol";
    import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_BRIDGEHUB_ADDR} from "../../../common/L2ContractAddresses.sol";
    import {IL1AssetRouter} from "../../../bridge/asset-router/IL1AssetRouter.sol";
    import {MerklePathEmpty, OnlyEraSupported, BatchNotExecuted, HashedLogIsDefault, BaseTokenGasPriceDenominatorNotSet, TransactionNotAllowed, GasPerPubdataMismatch, TooManyFactoryDeps, MsgValueTooLow, InvalidProofLengthForFinalNode} from "../../../common/L1ContractErrors.sol";
    import {NotL1, UnsupportedProofMetadataVersion, LocalRootIsZero, LocalRootMustBeZero, NotSettlementLayer, NotHyperchain} from "../../L1StateTransitionErrors.sol";
    // While formally the following import is not used, it is needed to inherit documentation from it
    import {IZKChainBase} from "../../chain-interfaces/IZKChainBase.sol";
    /// @title ZKsync Mailbox contract providing interfaces for L1 <-> L2 interaction.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    contract MailboxFacet is ZKChainBase, IMailbox {
        using UncheckedMath for uint256;
        using PriorityQueue for PriorityQueue.Queue;
        using PriorityTree for PriorityTree.Tree;
        /// @inheritdoc IZKChainBase
        string public constant override getName = "MailboxFacet";
        /// @dev Era's chainID
        uint256 internal immutable ERA_CHAIN_ID;
        /// @notice The chain id of L1. This contract can be deployed on multiple layers, but this value is still equal to the
        /// L1 that is at the most base layer.
        uint256 internal immutable L1_CHAIN_ID;
        modifier onlyL1() {
            if (block.chainid != L1_CHAIN_ID) {
                revert NotL1(block.chainid);
            }
            _;
        }
        constructor(uint256 _eraChainId, uint256 _l1ChainId) {
            ERA_CHAIN_ID = _eraChainId;
            L1_CHAIN_ID = _l1ChainId;
        }
        /// @inheritdoc IMailbox
        function bridgehubRequestL2Transaction(
            BridgehubL2TransactionRequest calldata _request
        ) external onlyBridgehub returns (bytes32 canonicalTxHash) {
            canonicalTxHash = _requestL2TransactionSender(_request);
        }
        /// @inheritdoc IMailbox
        function proveL2MessageInclusion(
            uint256 _batchNumber,
            uint256 _index,
            L2Message calldata _message,
            bytes32[] calldata _proof
        ) public view returns (bool) {
            return _proveL2LogInclusion(_batchNumber, _index, _L2MessageToLog(_message), _proof);
        }
        /// @inheritdoc IMailbox
        function proveL2LogInclusion(
            uint256 _batchNumber,
            uint256 _index,
            L2Log calldata _log,
            bytes32[] calldata _proof
        ) external view returns (bool) {
            return _proveL2LogInclusion(_batchNumber, _index, _log, _proof);
        }
        /// @inheritdoc IMailbox
        function proveL1ToL2TransactionStatus(
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof,
            TxStatus _status
        ) public view returns (bool) {
            // Bootloader sends an L2 -> L1 log only after processing the L1 -> L2 transaction.
            // Thus, we can verify that the L1 -> L2 transaction was included in the L2 batch with specified status.
            //
            // The semantics of such L2 -> L1 log is always:
            // - sender = L2_BOOTLOADER_ADDRESS
            // - key = hash(L1ToL2Transaction)
            // - value = status of the processing transaction (1 - success & 0 - fail)
            // - isService = true (just a conventional value)
            // - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard, other shards are not available yet anyway)
            // - txNumberInBatch = number of transaction in the batch
            L2Log memory l2Log = L2Log({
                l2ShardId: 0,
                isService: true,
                txNumberInBatch: _l2TxNumberInBatch,
                sender: L2_BOOTLOADER_ADDRESS,
                key: _l2TxHash,
                value: bytes32(uint256(_status))
            });
            return _proveL2LogInclusion(_l2BatchNumber, _l2MessageIndex, l2Log, _merkleProof);
        }
        function _parseProofMetadata(
            bytes32[] calldata _proof
        )
            internal
            pure
            returns (uint256 proofStartIndex, uint256 logLeafProofLen, uint256 batchLeafProofLen, bool finalProofNode)
        {
            bytes32 proofMetadata = _proof[0];
            // We support two formats of the proofs:
            // 1. The old format, where `_proof` is just a plain Merkle proof.
            // 2. The new format, where the first element of the `_proof` is encoded metadata, which consists of the following:
            // - first byte: metadata version (0x01).
            // - second byte: length of the log leaf proof (the proof that the log belongs to a batch).
            // - third byte: length of the batch leaf proof (the proof that the batch belongs to another settlement layer, if any).
            // - fourth byte: whether the current proof is the last in the links of recursive proofs for settlement layers.
            // - the rest of the bytes are zeroes.
            //
            // In the future the old version will be disabled, and only the new version will be supported.
            // For now, we need to support both for backwards compatibility. We distinguish between those based on whether the last 28 bytes are zeroes.
            // It is safe, since the elements of the proof are hashes and are unlikely to have 28 zero bytes in them.
            // We shift left by 4 bytes = 32 bits to remove the top 32 bits of the metadata.
            uint256 metadataAsUint256 = (uint256(proofMetadata) << 32);
            if (metadataAsUint256 == 0) {
                // It is the new version
                bytes1 metadataVersion = bytes1(proofMetadata);
                if (uint256(uint8(metadataVersion)) != SUPPORTED_PROOF_METADATA_VERSION) {
                    revert UnsupportedProofMetadataVersion(uint256(uint8(metadataVersion)));
                }
                proofStartIndex = 1;
                logLeafProofLen = uint256(uint8(proofMetadata[1]));
                batchLeafProofLen = uint256(uint8(proofMetadata[2]));
                finalProofNode = uint256(uint8(proofMetadata[3])) != 0;
            } else {
                // It is the old version
                // The entire proof is a merkle path
                proofStartIndex = 0;
                logLeafProofLen = _proof.length;
                batchLeafProofLen = 0;
                finalProofNode = true;
            }
            if (finalProofNode && batchLeafProofLen != 0) {
                revert InvalidProofLengthForFinalNode();
            }
        }
        function extractSlice(
            bytes32[] calldata _proof,
            uint256 _left,
            uint256 _right
        ) internal pure returns (bytes32[] memory slice) {
            slice = new bytes32[](_right - _left);
            for (uint256 i = _left; i < _right; i = i.uncheckedInc()) {
                slice[i - _left] = _proof[i];
            }
        }
        /// @notice Extracts slice until the end of the array.
        /// @dev It is used in one place in order to circumvent the stack too deep error.
        function extractSliceUntilEnd(
            bytes32[] calldata _proof,
            uint256 _start
        ) internal pure returns (bytes32[] memory slice) {
            slice = extractSlice(_proof, _start, _proof.length);
        }
        /// @inheritdoc IMailbox
        function proveL2LeafInclusion(
            uint256 _batchNumber,
            uint256 _leafProofMask,
            bytes32 _leaf,
            bytes32[] calldata _proof
        ) external view override returns (bool) {
            return _proveL2LeafInclusion(_batchNumber, _leafProofMask, _leaf, _proof);
        }
        function _proveL2LeafInclusion(
            uint256 _batchNumber,
            uint256 _leafProofMask,
            bytes32 _leaf,
            bytes32[] calldata _proof
        ) internal view returns (bool) {
            if (_proof.length == 0) {
                revert MerklePathEmpty();
            }
            uint256 ptr = 0;
            bytes32 chainIdLeaf;
            {
                (
                    uint256 proofStartIndex,
                    uint256 logLeafProofLen,
                    uint256 batchLeafProofLen,
                    bool finalProofNode
                ) = _parseProofMetadata(_proof);
                ptr = proofStartIndex;
                bytes32 batchSettlementRoot = Merkle.calculateRootMemory(
                    extractSlice(_proof, ptr, ptr + logLeafProofLen),
                    _leafProofMask,
                    _leaf
                );
                ptr += logLeafProofLen;
                // If the `finalProofNode` is true, then we assume that this is L1 contract of the top-level
                // in the aggregation, i.e. the batch root is stored here on L1.
                if (finalProofNode) {
                    // Double checking that the batch has been executed.
                    if (_batchNumber > s.totalBatchesExecuted) {
                        revert BatchNotExecuted(_batchNumber);
                    }
                    bytes32 correctBatchRoot = s.l2LogsRootHashes[_batchNumber];
                    if (correctBatchRoot == bytes32(0)) {
                        revert LocalRootIsZero();
                    }
                    return correctBatchRoot == batchSettlementRoot;
                }
                if (s.l2LogsRootHashes[_batchNumber] != bytes32(0)) {
                    revert LocalRootMustBeZero();
                }
                // Now, we'll have to check that the Gateway included the message.
                bytes32 batchLeafHash = MessageHashing.batchLeafHash(batchSettlementRoot, _batchNumber);
                uint256 batchLeafProofMask = uint256(bytes32(_proof[ptr]));
                ++ptr;
                bytes32 chainIdRoot = Merkle.calculateRootMemory(
                    extractSlice(_proof, ptr, ptr + batchLeafProofLen),
                    batchLeafProofMask,
                    batchLeafHash
                );
                ptr += batchLeafProofLen;
                chainIdLeaf = MessageHashing.chainIdLeafHash(chainIdRoot, s.chainId);
            }
            uint256 settlementLayerBatchNumber;
            uint256 settlementLayerBatchRootMask;
            address settlementLayerAddress;
            // Preventing stack too deep error
            {
                // Now, we just need to double check whether this chainId leaf was present in the tree.
                uint256 settlementLayerPackedBatchInfo = uint256(_proof[ptr]);
                ++ptr;
                settlementLayerBatchNumber = uint256(settlementLayerPackedBatchInfo >> 128);
                settlementLayerBatchRootMask = uint256(settlementLayerPackedBatchInfo & ((1 << 128) - 1));
                uint256 settlementLayerChainId = uint256(_proof[ptr]);
                ++ptr;
                // Assuming that `settlementLayerChainId` is an honest chain, the `chainIdLeaf` should belong
                // to a chain's message root only if the chain has indeed executed its batch on top of it.
                //
                // We trust all chains whitelisted by the Bridgehub governance.
                if (!IBridgehub(s.bridgehub).whitelistedSettlementLayers(settlementLayerChainId)) {
                    revert NotSettlementLayer();
                }
                settlementLayerAddress = IBridgehub(s.bridgehub).getZKChain(settlementLayerChainId);
            }
            return
                IMailbox(settlementLayerAddress).proveL2LeafInclusion(
                    settlementLayerBatchNumber,
                    settlementLayerBatchRootMask,
                    chainIdLeaf,
                    extractSliceUntilEnd(_proof, ptr)
                );
        }
        /// @dev Prove that a specific L2 log was sent in a specific L2 batch number
        function _proveL2LogInclusion(
            uint256 _batchNumber,
            uint256 _index,
            L2Log memory _log,
            bytes32[] calldata _proof
        ) internal view returns (bool) {
            bytes32 hashedLog = keccak256(
                // solhint-disable-next-line func-named-parameters
                abi.encodePacked(_log.l2ShardId, _log.isService, _log.txNumberInBatch, _log.sender, _log.key, _log.value)
            );
            // Check that hashed log is not the default one,
            // otherwise it means that the value is out of range of sent L2 -> L1 logs
            if (hashedLog == L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH) {
                revert HashedLogIsDefault();
            }
            // It is ok to not check length of `_proof` array, as length
            // of leaf preimage (which is `L2_TO_L1_LOG_SERIALIZE_SIZE`) is not
            // equal to the length of other nodes preimages (which are `2 * 32`)
            // We can use `index` as a mask, since the `localMessageRoot` is on the left part of the tree.
            return _proveL2LeafInclusion(_batchNumber, _index, hashedLog, _proof);
        }
        /// @dev Convert arbitrary-length message to the raw l2 log
        function _L2MessageToLog(L2Message calldata _message) internal pure returns (L2Log memory) {
            return
                L2Log({
                    l2ShardId: 0,
                    isService: true,
                    txNumberInBatch: _message.txNumberInBatch,
                    sender: address(L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR),
                    key: bytes32(uint256(uint160(_message.sender))),
                    value: keccak256(_message.data)
                });
        }
        /// @inheritdoc IMailbox
        function l2TransactionBaseCost(
            uint256 _gasPrice,
            uint256 _l2GasLimit,
            uint256 _l2GasPerPubdataByteLimit
        ) public view returns (uint256) {
            uint256 l2GasPrice = _deriveL2GasPrice(_gasPrice, _l2GasPerPubdataByteLimit);
            return l2GasPrice * _l2GasLimit;
        }
        /// @notice Derives the price for L2 gas in base token to be paid.
        /// @param _l1GasPrice The gas price on L1
        /// @param _gasPerPubdata The price for each pubdata byte in L2 gas
        /// @return The price of L2 gas in the base token
        function _deriveL2GasPrice(uint256 _l1GasPrice, uint256 _gasPerPubdata) internal view returns (uint256) {
            FeeParams memory feeParams = s.feeParams;
            if (s.baseTokenGasPriceMultiplierDenominator == 0) {
                revert BaseTokenGasPriceDenominatorNotSet();
            }
            uint256 l1GasPriceConverted = (_l1GasPrice * s.baseTokenGasPriceMultiplierNominator) /
                s.baseTokenGasPriceMultiplierDenominator;
            uint256 pubdataPriceBaseToken;
            if (feeParams.pubdataPricingMode == PubdataPricingMode.Rollup) {
                // slither-disable-next-line divide-before-multiply
                pubdataPriceBaseToken = L1_GAS_PER_PUBDATA_BYTE * l1GasPriceConverted;
            }
            // slither-disable-next-line divide-before-multiply
            uint256 batchOverheadBaseToken = uint256(feeParams.batchOverheadL1Gas) * l1GasPriceConverted;
            uint256 fullPubdataPriceBaseToken = pubdataPriceBaseToken +
                batchOverheadBaseToken /
                uint256(feeParams.maxPubdataPerBatch);
            uint256 l2GasPrice = feeParams.minimalL2GasPrice + batchOverheadBaseToken / uint256(feeParams.maxL2GasPerBatch);
            uint256 minL2GasPriceBaseToken = (fullPubdataPriceBaseToken + _gasPerPubdata - 1) / _gasPerPubdata;
            return Math.max(l2GasPrice, minL2GasPriceBaseToken);
        }
        /// @inheritdoc IMailbox
        function requestL2TransactionToGatewayMailbox(
            uint256 _chainId,
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) external override onlyL1 returns (bytes32 canonicalTxHash) {
            if (!IBridgehub(s.bridgehub).whitelistedSettlementLayers(s.chainId)) {
                revert NotSettlementLayer();
            }
            if (IChainTypeManager(s.chainTypeManager).getZKChain(_chainId) != msg.sender) {
                revert NotHyperchain();
            }
            BridgehubL2TransactionRequest memory wrappedRequest = _wrapRequest({
                _chainId: _chainId,
                _canonicalTxHash: _canonicalTxHash,
                _expirationTimestamp: _expirationTimestamp
            });
            canonicalTxHash = _requestL2TransactionFree(wrappedRequest);
        }
        /// @inheritdoc IMailbox
        function bridgehubRequestL2TransactionOnGateway(
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) external override onlyBridgehub {
            _writePriorityOpHash(_canonicalTxHash, _expirationTimestamp);
            emit NewRelayedPriorityTransaction(_getTotalPriorityTxs(), _canonicalTxHash, _expirationTimestamp);
        }
        function _wrapRequest(
            uint256 _chainId,
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) internal view returns (BridgehubL2TransactionRequest memory) {
            // solhint-disable-next-line func-named-parameters
            bytes memory data = abi.encodeCall(
                IBridgehub.forwardTransactionOnGateway,
                (_chainId, _canonicalTxHash, _expirationTimestamp)
            );
            return
                BridgehubL2TransactionRequest({
                    /// There is no sender for the wrapping, we use a virtual address.
                    sender: SETTLEMENT_LAYER_RELAY_SENDER,
                    contractL2: L2_BRIDGEHUB_ADDR,
                    mintValue: 0,
                    l2Value: 0,
                    // Very large amount
                    l2GasLimit: 72_000_000,
                    l2Calldata: data,
                    l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA,
                    factoryDeps: new bytes[](0),
                    // Tx is free, no so refund recipient needed
                    refundRecipient: address(0)
                });
        }
        ///  @inheritdoc IMailbox
        function requestL2ServiceTransaction(
            address _contractL2,
            bytes calldata _l2Calldata
        ) external onlySelf returns (bytes32 canonicalTxHash) {
            canonicalTxHash = _requestL2TransactionFree(
                BridgehubL2TransactionRequest({
                    sender: SERVICE_TRANSACTION_SENDER,
                    contractL2: _contractL2,
                    mintValue: 0,
                    l2Value: 0,
                    // Very large amount
                    l2GasLimit: 72_000_000,
                    l2Calldata: _l2Calldata,
                    l2GasPerPubdataByteLimit: REQUIRED_L2_GAS_PRICE_PER_PUBDATA,
                    factoryDeps: new bytes[](0),
                    // Tx is free, so no refund recipient needed
                    refundRecipient: address(0)
                })
            );
            if (s.settlementLayer != address(0)) {
                // slither-disable-next-line unused-return
                IMailbox(s.settlementLayer).requestL2TransactionToGatewayMailbox({
                    _chainId: s.chainId,
                    _canonicalTxHash: canonicalTxHash,
                    _expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION)
                });
            }
        }
        function _requestL2TransactionSender(
            BridgehubL2TransactionRequest memory _request
        ) internal nonReentrant returns (bytes32 canonicalTxHash) {
            // Check that the transaction is allowed by the filterer (if the filterer is set).
            if (s.transactionFilterer != address(0)) {
                if (
                    !ITransactionFilterer(s.transactionFilterer).isTransactionAllowed({
                        sender: _request.sender,
                        contractL2: _request.contractL2,
                        mintValue: _request.mintValue,
                        l2Value: _request.l2Value,
                        l2Calldata: _request.l2Calldata,
                        refundRecipient: _request.refundRecipient
                    })
                ) {
                    revert TransactionNotAllowed();
                }
            }
            // Enforcing that `_request.l2GasPerPubdataByteLimit` equals to a certain constant number. This is needed
            // to ensure that users do not get used to using "exotic" numbers for _request.l2GasPerPubdataByteLimit, e.g. 1-2, etc.
            // VERY IMPORTANT: nobody should rely on this constant to be fixed and every contract should give their users the ability to provide the
            // ability to provide `_request.l2GasPerPubdataByteLimit` for each independent transaction.
            // CHANGING THIS CONSTANT SHOULD BE A CLIENT-SIDE CHANGE.
            if (_request.l2GasPerPubdataByteLimit != REQUIRED_L2_GAS_PRICE_PER_PUBDATA) {
                revert GasPerPubdataMismatch();
            }
            WritePriorityOpParams memory params;
            params.request = _request;
            canonicalTxHash = _requestL2Transaction(params);
        }
        function _requestL2Transaction(WritePriorityOpParams memory _params) internal returns (bytes32 canonicalTxHash) {
            BridgehubL2TransactionRequest memory request = _params.request;
            if (request.factoryDeps.length > MAX_NEW_FACTORY_DEPS) {
                revert TooManyFactoryDeps();
            }
            _params.txId = _nextPriorityTxId();
            // Checking that the user provided enough ether to pay for the transaction.
            _params.l2GasPrice = _deriveL2GasPrice(tx.gasprice, request.l2GasPerPubdataByteLimit);
            uint256 baseCost = _params.l2GasPrice * request.l2GasLimit;
            if (request.mintValue < baseCost + request.l2Value) {
                revert MsgValueTooLow(baseCost + request.l2Value, request.mintValue);
            }
            request.refundRecipient = AddressAliasHelper.actualRefundRecipient(request.refundRecipient, request.sender);
            // Change the sender address if it is a smart contract to prevent address collision between L1 and L2.
            // Please note, currently ZKsync address derivation is different from Ethereum one, but it may be changed in the future.
            // solhint-disable avoid-tx-origin
            // slither-disable-next-line tx-origin
            if (request.sender != tx.origin) {
                request.sender = AddressAliasHelper.applyL1ToL2Alias(request.sender);
            }
            // populate missing fields
            _params.expirationTimestamp = uint64(block.timestamp + PRIORITY_EXPIRATION); // Safe to cast
            L2CanonicalTransaction memory transaction;
            (transaction, canonicalTxHash) = _validateTx(_params);
            _writePriorityOp(transaction, _params.request.factoryDeps, canonicalTxHash, _params.expirationTimestamp);
            if (s.settlementLayer != address(0)) {
                // slither-disable-next-line unused-return
                IMailbox(s.settlementLayer).requestL2TransactionToGatewayMailbox({
                    _chainId: s.chainId,
                    _canonicalTxHash: canonicalTxHash,
                    _expirationTimestamp: _params.expirationTimestamp
                });
            }
        }
        function _nextPriorityTxId() internal view returns (uint256) {
            if (_isPriorityQueueActive()) {
                return s.priorityQueue.getTotalPriorityTxs();
            } else {
                return s.priorityTree.getTotalPriorityTxs();
            }
        }
        function _requestL2TransactionFree(
            BridgehubL2TransactionRequest memory _request
        ) internal nonReentrant returns (bytes32 canonicalTxHash) {
            WritePriorityOpParams memory params = WritePriorityOpParams({
                request: _request,
                txId: _nextPriorityTxId(),
                l2GasPrice: 0,
                expirationTimestamp: uint64(block.timestamp + PRIORITY_EXPIRATION)
            });
            L2CanonicalTransaction memory transaction;
            (transaction, canonicalTxHash) = _validateTx(params);
            _writePriorityOp(transaction, params.request.factoryDeps, canonicalTxHash, params.expirationTimestamp);
        }
        function _serializeL2Transaction(
            WritePriorityOpParams memory _priorityOpParams
        ) internal pure returns (L2CanonicalTransaction memory transaction) {
            BridgehubL2TransactionRequest memory request = _priorityOpParams.request;
            transaction = L2CanonicalTransaction({
                txType: PRIORITY_OPERATION_L2_TX_TYPE,
                from: uint256(uint160(request.sender)),
                to: uint256(uint160(request.contractL2)),
                gasLimit: request.l2GasLimit,
                gasPerPubdataByteLimit: request.l2GasPerPubdataByteLimit,
                maxFeePerGas: uint256(_priorityOpParams.l2GasPrice),
                maxPriorityFeePerGas: uint256(0),
                paymaster: uint256(0),
                // Note, that the priority operation id is used as "nonce" for L1->L2 transactions
                nonce: uint256(_priorityOpParams.txId),
                value: request.l2Value,
                reserved: [request.mintValue, uint256(uint160(request.refundRecipient)), 0, 0],
                data: request.l2Calldata,
                signature: new bytes(0),
                factoryDeps: _hashFactoryDeps(request.factoryDeps),
                paymasterInput: new bytes(0),
                reservedDynamic: new bytes(0)
            });
        }
        function _validateTx(
            WritePriorityOpParams memory _priorityOpParams
        ) internal view returns (L2CanonicalTransaction memory transaction, bytes32 canonicalTxHash) {
            transaction = _serializeL2Transaction(_priorityOpParams);
            bytes memory transactionEncoding = abi.encode(transaction);
            TransactionValidator.validateL1ToL2Transaction(
                transaction,
                transactionEncoding,
                s.priorityTxMaxGasLimit,
                s.feeParams.priorityTxMaxPubdata
            );
            canonicalTxHash = keccak256(transactionEncoding);
        }
        /// @notice Stores a transaction record in storage & send event about that
        function _writePriorityOp(
            L2CanonicalTransaction memory _transaction,
            bytes[] memory _factoryDeps,
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) internal {
            _writePriorityOpHash(_canonicalTxHash, _expirationTimestamp);
            // Data that is needed for the operator to simulate priority queue offchain
            // solhint-disable-next-line func-named-parameters
            emit NewPriorityRequest(_transaction.nonce, _canonicalTxHash, _expirationTimestamp, _transaction, _factoryDeps);
        }
        function _writePriorityOpHash(bytes32 _canonicalTxHash, uint64 _expirationTimestamp) internal {
            if (_isPriorityQueueActive()) {
                s.priorityQueue.pushBack(
                    PriorityOperation({
                        canonicalTxHash: _canonicalTxHash,
                        expirationTimestamp: _expirationTimestamp,
                        layer2Tip: uint192(0) // TODO: Restore after fee modeling will be stable. (SMA-1230)
                    })
                );
            }
            s.priorityTree.push(_canonicalTxHash);
        }
        /// @notice Hashes the L2 bytecodes and returns them in the format in which they are processed by the bootloader
        function _hashFactoryDeps(bytes[] memory _factoryDeps) internal pure returns (uint256[] memory hashedFactoryDeps) {
            uint256 factoryDepsLen = _factoryDeps.length;
            hashedFactoryDeps = new uint256[](factoryDepsLen);
            for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
                bytes32 hashedBytecode = L2ContractHelper.hashL2Bytecode(_factoryDeps[i]);
                // Store the resulting hash sequentially in bytes.
                assembly {
                    mstore(add(hashedFactoryDeps, mul(add(i, 1), 32)), hashedBytecode)
                }
            }
        }
        ///////////////////////////////////////////////////////
        //////// Legacy Era functions
        /// @inheritdoc IMailbox
        function finalizeEthWithdrawal(
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes calldata _message,
            bytes32[] calldata _merkleProof
        ) external nonReentrant onlyL1 {
            if (s.chainId != ERA_CHAIN_ID) {
                revert OnlyEraSupported();
            }
            address sharedBridge = IBridgehub(s.bridgehub).sharedBridge();
            IL1AssetRouter(sharedBridge).finalizeWithdrawal({
                _chainId: ERA_CHAIN_ID,
                _l2BatchNumber: _l2BatchNumber,
                _l2MessageIndex: _l2MessageIndex,
                _l2TxNumberInBatch: _l2TxNumberInBatch,
                _message: _message,
                _merkleProof: _merkleProof
            });
        }
        ///  @inheritdoc IMailbox
        function requestL2Transaction(
            address _contractL2,
            uint256 _l2Value,
            bytes calldata _calldata,
            uint256 _l2GasLimit,
            uint256 _l2GasPerPubdataByteLimit,
            bytes[] calldata _factoryDeps,
            address _refundRecipient
        ) external payable onlyL1 returns (bytes32 canonicalTxHash) {
            if (s.chainId != ERA_CHAIN_ID) {
                revert OnlyEraSupported();
            }
            canonicalTxHash = _requestL2TransactionSender(
                BridgehubL2TransactionRequest({
                    sender: msg.sender,
                    contractL2: _contractL2,
                    mintValue: msg.value,
                    l2Value: _l2Value,
                    l2GasLimit: _l2GasLimit,
                    l2Calldata: _calldata,
                    l2GasPerPubdataByteLimit: _l2GasPerPubdataByteLimit,
                    factoryDeps: _factoryDeps,
                    refundRecipient: _refundRecipient
                })
            );
            address sharedBridge = IBridgehub(s.bridgehub).sharedBridge();
            IL1AssetRouter(sharedBridge).bridgehubDepositBaseToken{value: msg.value}(
                s.chainId,
                s.baseTokenAssetId,
                msg.sender,
                msg.value
            );
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Standard math utilities missing in the Solidity language.
     */
    library Math {
        enum Rounding {
            Down, // Toward negative infinity
            Up, // Toward infinity
            Zero // Toward zero
        }
        /**
         * @dev Returns the largest of two numbers.
         */
        function max(uint256 a, uint256 b) internal pure returns (uint256) {
            return a > b ? a : b;
        }
        /**
         * @dev Returns the smallest of two numbers.
         */
        function min(uint256 a, uint256 b) internal pure returns (uint256) {
            return a < b ? a : b;
        }
        /**
         * @dev Returns the average of two numbers. The result is rounded towards
         * zero.
         */
        function average(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b) / 2 can overflow.
            return (a & b) + (a ^ b) / 2;
        }
        /**
         * @dev Returns the ceiling of the division of two numbers.
         *
         * This differs from standard division with `/` in that it rounds up instead
         * of rounding down.
         */
        function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
            // (a + b - 1) / b can overflow on addition, so we distribute.
            return a == 0 ? 0 : (a - 1) / b + 1;
        }
        /**
         * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
         * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
         * with further edits by Uniswap Labs also under MIT license.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
            unchecked {
                // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                // variables such that product = prod1 * 2^256 + prod0.
                uint256 prod0; // Least significant 256 bits of the product
                uint256 prod1; // Most significant 256 bits of the product
                assembly {
                    let mm := mulmod(x, y, not(0))
                    prod0 := mul(x, y)
                    prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                }
                // Handle non-overflow cases, 256 by 256 division.
                if (prod1 == 0) {
                    // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                    // The surrounding unchecked block does not change this fact.
                    // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                    return prod0 / denominator;
                }
                // Make sure the result is less than 2^256. Also prevents denominator == 0.
                require(denominator > prod1, "Math: mulDiv overflow");
                ///////////////////////////////////////////////
                // 512 by 256 division.
                ///////////////////////////////////////////////
                // Make division exact by subtracting the remainder from [prod1 prod0].
                uint256 remainder;
                assembly {
                    // Compute remainder using mulmod.
                    remainder := mulmod(x, y, denominator)
                    // Subtract 256 bit number from 512 bit number.
                    prod1 := sub(prod1, gt(remainder, prod0))
                    prod0 := sub(prod0, remainder)
                }
                // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                // See https://cs.stackexchange.com/q/138556/92363.
                // Does not overflow because the denominator cannot be zero at this stage in the function.
                uint256 twos = denominator & (~denominator + 1);
                assembly {
                    // Divide denominator by twos.
                    denominator := div(denominator, twos)
                    // Divide [prod1 prod0] by twos.
                    prod0 := div(prod0, twos)
                    // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                    twos := add(div(sub(0, twos), twos), 1)
                }
                // Shift in bits from prod1 into prod0.
                prod0 |= prod1 * twos;
                // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                // four bits. That is, denominator * inv = 1 mod 2^4.
                uint256 inverse = (3 * denominator) ^ 2;
                // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                // in modular arithmetic, doubling the correct bits in each step.
                inverse *= 2 - denominator * inverse; // inverse mod 2^8
                inverse *= 2 - denominator * inverse; // inverse mod 2^16
                inverse *= 2 - denominator * inverse; // inverse mod 2^32
                inverse *= 2 - denominator * inverse; // inverse mod 2^64
                inverse *= 2 - denominator * inverse; // inverse mod 2^128
                inverse *= 2 - denominator * inverse; // inverse mod 2^256
                // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                // is no longer required.
                result = prod0 * inverse;
                return result;
            }
        }
        /**
         * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
         */
        function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
            uint256 result = mulDiv(x, y, denominator);
            if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                result += 1;
            }
            return result;
        }
        /**
         * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
         *
         * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
         */
        function sqrt(uint256 a) internal pure returns (uint256) {
            if (a == 0) {
                return 0;
            }
            // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
            //
            // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
            // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
            //
            // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
            // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
            // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
            //
            // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
            uint256 result = 1 << (log2(a) >> 1);
            // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
            // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
            // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
            // into the expected uint128 result.
            unchecked {
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                result = (result + a / result) >> 1;
                return min(result, a / result);
            }
        }
        /**
         * @notice Calculates sqrt(a), following the selected rounding direction.
         */
        function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = sqrt(a);
                return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 2, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 128;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 64;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 32;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 16;
                }
                if (value >> 8 > 0) {
                    value >>= 8;
                    result += 8;
                }
                if (value >> 4 > 0) {
                    value >>= 4;
                    result += 4;
                }
                if (value >> 2 > 0) {
                    value >>= 2;
                    result += 2;
                }
                if (value >> 1 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log2(value);
                return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 10, rounded down, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >= 10 ** 64) {
                    value /= 10 ** 64;
                    result += 64;
                }
                if (value >= 10 ** 32) {
                    value /= 10 ** 32;
                    result += 32;
                }
                if (value >= 10 ** 16) {
                    value /= 10 ** 16;
                    result += 16;
                }
                if (value >= 10 ** 8) {
                    value /= 10 ** 8;
                    result += 8;
                }
                if (value >= 10 ** 4) {
                    value /= 10 ** 4;
                    result += 4;
                }
                if (value >= 10 ** 2) {
                    value /= 10 ** 2;
                    result += 2;
                }
                if (value >= 10 ** 1) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log10(value);
                return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
            }
        }
        /**
         * @dev Return the log in base 256, rounded down, of a positive value.
         * Returns 0 if given 0.
         *
         * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
         */
        function log256(uint256 value) internal pure returns (uint256) {
            uint256 result = 0;
            unchecked {
                if (value >> 128 > 0) {
                    value >>= 128;
                    result += 16;
                }
                if (value >> 64 > 0) {
                    value >>= 64;
                    result += 8;
                }
                if (value >> 32 > 0) {
                    value >>= 32;
                    result += 4;
                }
                if (value >> 16 > 0) {
                    value >>= 16;
                    result += 2;
                }
                if (value >> 8 > 0) {
                    result += 1;
                }
            }
            return result;
        }
        /**
         * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
         * Returns 0 if given 0.
         */
        function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
            unchecked {
                uint256 result = log256(value);
                return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {IZKChainBase} from "./IZKChainBase.sol";
    import {L2CanonicalTransaction, L2Log, L2Message, TxStatus, BridgehubL2TransactionRequest} from "../../common/Messaging.sol";
    /// @title The interface of the ZKsync Mailbox contract that provides interfaces for L1 <-> L2 interaction.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IMailbox is IZKChainBase {
        /// @notice Prove that a specific arbitrary-length message was sent in a specific L2 batch number
        /// @param _batchNumber The executed L2 batch number in which the message appeared
        /// @param _index The position in the L2 logs Merkle tree of the l2Log that was sent with the message
        /// @param _message Information about the sent message: sender address, the message itself, tx index in the L2 batch where the message was sent
        /// @param _proof Merkle proof for inclusion of L2 log that was sent with the message
        /// @return Whether the proof is valid
        function proveL2MessageInclusion(
            uint256 _batchNumber,
            uint256 _index,
            L2Message calldata _message,
            bytes32[] calldata _proof
        ) external view returns (bool);
        /// @notice Prove that a specific L2 log was sent in a specific L2 batch
        /// @param _batchNumber The executed L2 batch number in which the log appeared
        /// @param _index The position of the l2log in the L2 logs Merkle tree
        /// @param _log Information about the sent log
        /// @param _proof Merkle proof for inclusion of the L2 log
        /// @return Whether the proof is correct and L2 log is included in batch
        function proveL2LogInclusion(
            uint256 _batchNumber,
            uint256 _index,
            L2Log memory _log,
            bytes32[] calldata _proof
        ) external view returns (bool);
        /// @notice Prove that the L1 -> L2 transaction was processed with the specified status.
        /// @param _l2TxHash The L2 canonical transaction hash
        /// @param _l2BatchNumber The L2 batch number where the transaction was processed
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
        /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent
        /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction
        /// @param _status The execution status of the L1 -> L2 transaction (true - success & 0 - fail)
        /// @return Whether the proof is correct and the transaction was actually executed with provided status
        /// NOTE: It may return `false` for incorrect proof, but it doesn't mean that the L1 -> L2 transaction has an opposite status!
        function proveL1ToL2TransactionStatus(
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof,
            TxStatus _status
        ) external view returns (bool);
        /// @notice Finalize the withdrawal and release funds
        /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
        /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent
        /// @param _message The L2 withdraw data, stored in an L2 -> L1 message
        /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
        function finalizeEthWithdrawal(
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes calldata _message,
            bytes32[] calldata _merkleProof
        ) external;
        /// @notice Request execution of L2 transaction from L1.
        /// @param _contractL2 The L2 receiver address
        /// @param _l2Value `msg.value` of L2 transaction
        /// @param _calldata The input of the L2 transaction
        /// @param _l2GasLimit Maximum amount of L2 gas that transaction can consume during execution on L2
        /// @param _l2GasPerPubdataByteLimit The maximum amount L2 gas that the operator may charge the user for single byte of pubdata.
        /// @param _factoryDeps An array of L2 bytecodes that will be marked as known on L2
        /// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
        /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`.
        /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses out of control.
        /// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`.
        /// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will be sent to the `msg.sender` address.
        /// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be sent to the aliased `msg.sender` address.
        /// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds are controllable,
        /// since address aliasing to the from address for the L2 tx will be applied if the L1 `msg.sender` is a contract.
        /// Without address aliasing for L1 contracts as refund recipients they would not be able to make proper L2 tx requests
        /// through the Mailbox to use or withdraw the funds from L2, and the funds would be lost.
        /// @return canonicalTxHash The hash of the requested L2 transaction. This hash can be used to follow the transaction status
        function requestL2Transaction(
            address _contractL2,
            uint256 _l2Value,
            bytes calldata _calldata,
            uint256 _l2GasLimit,
            uint256 _l2GasPerPubdataByteLimit,
            bytes[] calldata _factoryDeps,
            address _refundRecipient
        ) external payable returns (bytes32 canonicalTxHash);
        /// @notice when requesting transactions through the bridgehub
        function bridgehubRequestL2Transaction(
            BridgehubL2TransactionRequest calldata _request
        ) external returns (bytes32 canonicalTxHash);
        /// @dev On the Gateway the chain's mailbox receives the tx from the bridgehub.
        function bridgehubRequestL2TransactionOnGateway(bytes32 _canonicalTxHash, uint64 _expirationTimestamp) external;
        /// @notice Request execution of service L2 transaction from L1.
        /// @dev Used for chain configuration. Can be called only by DiamondProxy itself.
        /// @param _contractL2 The L2 receiver address
        /// @param _l2Calldata The input of the L2 transaction
        function requestL2ServiceTransaction(
            address _contractL2,
            bytes calldata _l2Calldata
        ) external returns (bytes32 canonicalTxHash);
        /// @dev On L1 we have to forward to the Gateway's mailbox which sends to the Bridgehub on the Gw
        /// @param _chainId the chainId of the chain
        /// @param _canonicalTxHash the canonical transaction hash
        /// @param _expirationTimestamp the expiration timestamp
        function requestL2TransactionToGatewayMailbox(
            uint256 _chainId,
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) external returns (bytes32 canonicalTxHash);
        /// @notice Estimates the cost in Ether of requesting execution of an L2 transaction from L1
        /// @param _gasPrice expected L1 gas price at which the user requests the transaction execution
        /// @param _l2GasLimit Maximum amount of L2 gas that transaction can consume during execution on L2
        /// @param _l2GasPerPubdataByteLimit The maximum amount of L2 gas that the operator may charge the user for a single byte of pubdata.
        /// @return The estimated ETH spent on L2 gas for the transaction
        function l2TransactionBaseCost(
            uint256 _gasPrice,
            uint256 _l2GasLimit,
            uint256 _l2GasPerPubdataByteLimit
        ) external view returns (uint256);
        /// @dev Proves that a certain leaf was included as part of the log merkle tree.
        /// @dev Warning: this function does not enforce any additional checks on the structure
        /// of the leaf. This means that it can accept intermediate nodes of the Merkle tree as a `_leaf` as
        /// well as the default "empty" leaves. It is the responsibility of the caller to ensure that the
        /// `_leaf` is a hash of a valid leaf.
        function proveL2LeafInclusion(
            uint256 _batchNumber,
            uint256 _batchRootMask,
            bytes32 _leaf,
            bytes32[] calldata _proof
        ) external view returns (bool);
        /// @notice transfer Eth to shared bridge as part of migration process
        // function transferEthToSharedBridge() external;
        // function relayTxSL(
        //     address _to,
        //     L2CanonicalTransaction memory _transaction,
        //     bytes[] memory _factoryDeps,
        //     bytes32 _canonicalTxHash,
        //     uint64 _expirationTimestamp
        // ) external;
        // function freeAcceptTx(
        //     L2CanonicalTransaction memory _transaction,
        //     bytes[] memory _factoryDeps,
        //     bytes32 _canonicalTxHash,
        //     uint64 _expirationTimestamp
        // ) external;
        // function acceptFreeRequestFromBridgehub(BridgehubL2TransactionRequest calldata _request) external;
        /// @notice New priority request event. Emitted when a request is placed into the priority queue
        /// @param txId Serial number of the priority operation
        /// @param txHash keccak256 hash of encoded transaction representation
        /// @param expirationTimestamp Timestamp up to which priority request should be processed
        /// @param transaction The whole transaction structure that is requested to be executed on L2
        /// @param factoryDeps An array of bytecodes that were shown in the L1 public data.
        /// Will be marked as known bytecodes in L2
        event NewPriorityRequest(
            uint256 txId,
            bytes32 txHash,
            uint64 expirationTimestamp,
            L2CanonicalTransaction transaction,
            bytes[] factoryDeps
        );
        /// @notice New relayed priority request event. It is emitted on a chain that is deployed
        /// on top of the gateway when it receives a request relayed via the Bridgehub.
        /// @dev IMPORTANT: this event most likely will be removed in the future, so
        /// no one should rely on it for indexing purposes.
        /// @param txId Serial number of the priority operation
        /// @param txHash keccak256 hash of encoded transaction representation
        /// @param expirationTimestamp Timestamp up to which priority request should be processed
        event NewRelayedPriorityTransaction(uint256 txId, bytes32 txHash, uint64 expirationTimestamp);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {Diamond} from "./libraries/Diamond.sol";
    import {L2CanonicalTransaction} from "../common/Messaging.sol";
    import {FeeParams} from "./chain-deps/ZKChainStorage.sol";
    // import {IBridgehub} from "../bridgehub/IBridgehub.sol";
    /// @notice Struct that holds all data needed for initializing CTM Proxy.
    /// @dev We use struct instead of raw parameters in `initialize` function to prevent "Stack too deep" error
    /// @param owner The address who can manage non-critical updates in the contract
    /// @param validatorTimelock The address that serves as consensus, i.e. can submit blocks to be processed
    /// @param chainCreationParams The struct that contains the fields that define how a new chain should be created
    /// @param protocolVersion The initial protocol version on the newly deployed chain
    /// @param serverNotifier The address that serves as server notifier
    // solhint-disable-next-line gas-struct-packing
    struct ChainTypeManagerInitializeData {
        address owner;
        address validatorTimelock;
        ChainCreationParams chainCreationParams;
        uint256 protocolVersion;
        address serverNotifier;
    }
    /// @notice The struct that contains the fields that define how a new chain should be created
    /// within this CTM.
    /// @param genesisUpgrade The address that is used in the diamond cut initialize address on chain creation
    /// @param genesisBatchHash Batch hash of the genesis (initial) batch
    /// @param genesisIndexRepeatedStorageChanges The serial number of the shortcut storage key for the genesis batch
    /// @param genesisBatchCommitment The zk-proof commitment for the genesis batch
    /// @param diamondCut The diamond cut for the first upgrade transaction on the newly deployed chain
    // solhint-disable-next-line gas-struct-packing
    struct ChainCreationParams {
        address genesisUpgrade;
        bytes32 genesisBatchHash;
        uint64 genesisIndexRepeatedStorageChanges;
        bytes32 genesisBatchCommitment;
        Diamond.DiamondCutData diamondCut;
        bytes forceDeploymentsData;
    }
    interface IChainTypeManager {
        /// @dev Emitted when a new ZKChain is added
        event NewZKChain(uint256 indexed _chainId, address indexed _zkChainContract);
        /// @dev emitted when an chain registers and a GenesisUpgrade happens
        event GenesisUpgrade(
            address indexed _zkChain,
            L2CanonicalTransaction _l2Transaction,
            uint256 indexed _protocolVersion
        );
        /// @notice pendingAdmin is changed
        /// @dev Also emitted when new admin is accepted and in this case, `newPendingAdmin` would be zero address
        event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
        /// @notice Admin changed
        event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
        /// @notice ValidatorTimelock changed
        event NewValidatorTimelock(address indexed oldValidatorTimelock, address indexed newValidatorTimelock);
        /// @notice ServerNotifier changed
        event NewServerNotifier(address indexed oldServerNotifier, address indexed newServerNotifier);
        /// @notice chain creation parameters changed
        event NewChainCreationParams(
            address genesisUpgrade,
            bytes32 genesisBatchHash,
            uint64 genesisIndexRepeatedStorageChanges,
            bytes32 genesisBatchCommitment,
            bytes32 newInitialCutHash,
            bytes32 forceDeploymentHash
        );
        /// @notice New UpgradeCutHash
        event NewUpgradeCutHash(uint256 indexed protocolVersion, bytes32 indexed upgradeCutHash);
        /// @notice New UpgradeCutData
        event NewUpgradeCutData(uint256 indexed protocolVersion, Diamond.DiamondCutData diamondCutData);
        /// @notice New ProtocolVersion
        event NewProtocolVersion(uint256 indexed oldProtocolVersion, uint256 indexed newProtocolVersion);
        /// @notice Updated ProtocolVersion deadline
        event UpdateProtocolVersionDeadline(uint256 indexed protocolVersion, uint256 deadline);
        function BRIDGE_HUB() external view returns (address);
        function setPendingAdmin(address _newPendingAdmin) external;
        function acceptAdmin() external;
        function getZKChain(uint256 _chainId) external view returns (address);
        function getHyperchain(uint256 _chainId) external view returns (address);
        function getZKChainLegacy(uint256 _chainId) external view returns (address);
        function storedBatchZero() external view returns (bytes32);
        function initialCutHash() external view returns (bytes32);
        function l1GenesisUpgrade() external view returns (address);
        function upgradeCutHash(uint256 _protocolVersion) external view returns (bytes32);
        function protocolVersion() external view returns (uint256);
        function protocolVersionDeadline(uint256 _protocolVersion) external view returns (uint256);
        function protocolVersionIsActive(uint256 _protocolVersion) external view returns (bool);
        function getProtocolVersion(uint256 _chainId) external view returns (uint256);
        function initialize(ChainTypeManagerInitializeData calldata _initializeData) external;
        function setValidatorTimelock(address _validatorTimelock) external;
        function setChainCreationParams(ChainCreationParams calldata _chainCreationParams) external;
        function getChainAdmin(uint256 _chainId) external view returns (address);
        function createNewChain(
            uint256 _chainId,
            bytes32 _baseTokenAssetId,
            address _admin,
            bytes calldata _initData,
            bytes[] calldata _factoryDeps
        ) external returns (address);
        function setNewVersionUpgrade(
            Diamond.DiamondCutData calldata _cutData,
            uint256 _oldProtocolVersion,
            uint256 _oldProtocolVersionDeadline,
            uint256 _newProtocolVersion
        ) external;
        function setUpgradeDiamondCut(Diamond.DiamondCutData calldata _cutData, uint256 _oldProtocolVersion) external;
        function executeUpgrade(uint256 _chainId, Diamond.DiamondCutData calldata _diamondCut) external;
        function setPriorityTxMaxGasLimit(uint256 _chainId, uint256 _maxGasLimit) external;
        function freezeChain(uint256 _chainId) external;
        function unfreezeChain(uint256 _chainId) external;
        function setTokenMultiplier(uint256 _chainId, uint128 _nominator, uint128 _denominator) external;
        function changeFeeParams(uint256 _chainId, FeeParams calldata _newFeeParams) external;
        function setValidator(uint256 _chainId, address _validator, bool _active) external;
        function setPorterAvailability(uint256 _chainId, bool _zkPorterIsAvailable) external;
        function upgradeChainFromVersion(
            uint256 _chainId,
            uint256 _oldProtocolVersion,
            Diamond.DiamondCutData calldata _diamondCut
        ) external;
        function getSemverProtocolVersion() external view returns (uint32, uint32, uint32);
        function forwardedBridgeBurn(
            uint256 _chainId,
            bytes calldata _data
        ) external returns (bytes memory _bridgeMintData);
        function forwardedBridgeMint(uint256 _chainId, bytes calldata _data) external returns (address);
        function forwardedBridgeRecoverFailedTransfer(
            uint256 _chainId,
            bytes32 _assetInfo,
            address _depositSender,
            bytes calldata _ctmData
        ) external;
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {L2Message, L2Log, TxStatus} from "../common/Messaging.sol";
    import {IL1AssetHandler} from "../bridge/interfaces/IL1AssetHandler.sol";
    import {ICTMDeploymentTracker} from "./ICTMDeploymentTracker.sol";
    import {IMessageRoot} from "./IMessageRoot.sol";
    import {IAssetHandler} from "../bridge/interfaces/IAssetHandler.sol";
    struct L2TransactionRequestDirect {
        uint256 chainId;
        uint256 mintValue;
        address l2Contract;
        uint256 l2Value;
        bytes l2Calldata;
        uint256 l2GasLimit;
        uint256 l2GasPerPubdataByteLimit;
        bytes[] factoryDeps;
        address refundRecipient;
    }
    struct L2TransactionRequestTwoBridgesOuter {
        uint256 chainId;
        uint256 mintValue;
        uint256 l2Value;
        uint256 l2GasLimit;
        uint256 l2GasPerPubdataByteLimit;
        address refundRecipient;
        address secondBridgeAddress;
        uint256 secondBridgeValue;
        bytes secondBridgeCalldata;
    }
    struct L2TransactionRequestTwoBridgesInner {
        bytes32 magicValue;
        address l2Contract;
        bytes l2Calldata;
        bytes[] factoryDeps;
        bytes32 txDataHash;
    }
    struct BridgehubMintCTMAssetData {
        uint256 chainId;
        bytes32 baseTokenAssetId;
        bytes ctmData;
        bytes chainData;
    }
    struct BridgehubBurnCTMAssetData {
        uint256 chainId;
        bytes ctmData;
        bytes chainData;
    }
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IBridgehub is IAssetHandler, IL1AssetHandler {
        /// @notice pendingAdmin is changed
        /// @dev Also emitted when new admin is accepted and in this case, `newPendingAdmin` would be zero address
        event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
        /// @notice Admin changed
        event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
        /// @notice CTM asset registered
        event AssetRegistered(
            bytes32 indexed assetInfo,
            address indexed _assetAddress,
            bytes32 indexed additionalData,
            address sender
        );
        event SettlementLayerRegistered(uint256 indexed chainId, bool indexed isWhitelisted);
        /// @notice Emitted when the bridging to the chain is started.
        /// @param chainId Chain ID of the ZK chain
        /// @param assetId Asset ID of the token for the zkChain's CTM
        /// @param settlementLayerChainId The chain id of the settlement layer the chain migrates to.
        event MigrationStarted(uint256 indexed chainId, bytes32 indexed assetId, uint256 indexed settlementLayerChainId);
        /// @notice Emitted when the bridging to the chain is complete.
        /// @param chainId Chain ID of the ZK chain
        /// @param assetId Asset ID of the token for the zkChain's CTM
        /// @param zkChain The address of the ZK chain on the chain where it is migrated to.
        event MigrationFinalized(uint256 indexed chainId, bytes32 indexed assetId, address indexed zkChain);
        /// @notice Starts the transfer of admin rights. Only the current admin or owner can propose a new pending one.
        /// @notice New admin can accept admin rights by calling `acceptAdmin` function.
        /// @param _newPendingAdmin Address of the new admin
        function setPendingAdmin(address _newPendingAdmin) external;
        /// @notice Accepts transfer of admin rights. Only pending admin can accept the role.
        function acceptAdmin() external;
        /// Getters
        function chainTypeManagerIsRegistered(address _chainTypeManager) external view returns (bool);
        function chainTypeManager(uint256 _chainId) external view returns (address);
        function assetIdIsRegistered(bytes32 _baseTokenAssetId) external view returns (bool);
        function baseToken(uint256 _chainId) external view returns (address);
        function baseTokenAssetId(uint256 _chainId) external view returns (bytes32);
        function sharedBridge() external view returns (address);
        function messageRoot() external view returns (IMessageRoot);
        function getZKChain(uint256 _chainId) external view returns (address);
        function getAllZKChains() external view returns (address[] memory);
        function getAllZKChainChainIDs() external view returns (uint256[] memory);
        function migrationPaused() external view returns (bool);
        function admin() external view returns (address);
        function assetRouter() external view returns (address);
        /// Mailbox forwarder
        function proveL2MessageInclusion(
            uint256 _chainId,
            uint256 _batchNumber,
            uint256 _index,
            L2Message calldata _message,
            bytes32[] calldata _proof
        ) external view returns (bool);
        function proveL2LogInclusion(
            uint256 _chainId,
            uint256 _batchNumber,
            uint256 _index,
            L2Log memory _log,
            bytes32[] calldata _proof
        ) external view returns (bool);
        function proveL1ToL2TransactionStatus(
            uint256 _chainId,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof,
            TxStatus _status
        ) external view returns (bool);
        function requestL2TransactionDirect(
            L2TransactionRequestDirect calldata _request
        ) external payable returns (bytes32 canonicalTxHash);
        function requestL2TransactionTwoBridges(
            L2TransactionRequestTwoBridgesOuter calldata _request
        ) external payable returns (bytes32 canonicalTxHash);
        function l2TransactionBaseCost(
            uint256 _chainId,
            uint256 _gasPrice,
            uint256 _l2GasLimit,
            uint256 _l2GasPerPubdataByteLimit
        ) external view returns (uint256);
        //// Registry
        function createNewChain(
            uint256 _chainId,
            address _chainTypeManager,
            bytes32 _baseTokenAssetId,
            uint256 _salt,
            address _admin,
            bytes calldata _initData,
            bytes[] calldata _factoryDeps
        ) external returns (uint256 chainId);
        function addChainTypeManager(address _chainTypeManager) external;
        function removeChainTypeManager(address _chainTypeManager) external;
        function addTokenAssetId(bytes32 _baseTokenAssetId) external;
        function setAddresses(
            address _sharedBridge,
            ICTMDeploymentTracker _l1CtmDeployer,
            IMessageRoot _messageRoot
        ) external;
        event NewChain(uint256 indexed chainId, address chainTypeManager, address indexed chainGovernance);
        event ChainTypeManagerAdded(address indexed chainTypeManager);
        event ChainTypeManagerRemoved(address indexed chainTypeManager);
        event BaseTokenAssetIdRegistered(bytes32 indexed assetId);
        function whitelistedSettlementLayers(uint256 _chainId) external view returns (bool);
        function registerSettlementLayer(uint256 _newSettlementLayerChainId, bool _isWhitelisted) external;
        function settlementLayer(uint256 _chainId) external view returns (uint256);
        // function finalizeMigrationToGateway(
        //     uint256 _chainId,
        //     address _baseToken,
        //     address _sharedBridge,
        //     address _admin,
        //     uint256 _expectedProtocolVersion,
        //     ZKChainCommitment calldata _commitment,
        //     bytes calldata _diamondCut
        // ) external;
        function forwardTransactionOnGateway(
            uint256 _chainId,
            bytes32 _canonicalTxHash,
            uint64 _expirationTimestamp
        ) external;
        function ctmAssetIdFromChainId(uint256 _chainId) external view returns (bytes32);
        function ctmAssetIdFromAddress(address _ctmAddress) external view returns (bytes32);
        function l1CtmDeployer() external view returns (ICTMDeploymentTracker);
        function ctmAssetIdToAddress(bytes32 _assetInfo) external view returns (address);
        function setCTMAssetAddress(bytes32 _additionalData, address _assetAddress) external;
        function L1_CHAIN_ID() external view returns (uint256);
        function registerAlreadyDeployedZKChain(uint256 _chainId, address _hyperchain) external;
        /// @notice return the ZK chain contract for a chainId
        /// @dev It is a legacy method. Do not use!
        function getHyperchain(uint256 _chainId) external view returns (address);
        function registerLegacyChain(uint256 _chainId) external;
        function pauseMigration() external;
        function unpauseMigration() external;
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @title The interface of the L1 -> L2 transaction filterer.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface ITransactionFilterer {
        /// @notice Check if the transaction is allowed
        /// @param sender The sender of the transaction
        /// @param contractL2 The L2 receiver address
        /// @param mintValue The value of the L1 transaction
        /// @param l2Value The msg.value of the L2 transaction
        /// @param l2Calldata The calldata of the L2 transaction
        /// @param refundRecipient The address to refund the excess value
        /// @return Whether the transaction is allowed
        function isTransactionAllowed(
            address sender,
            address contractL2,
            uint256 mintValue,
            uint256 l2Value,
            bytes memory l2Calldata,
            address refundRecipient
        ) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol";
    import {MerklePathEmpty, MerklePathOutOfBounds, MerkleIndexOutOfBounds, MerklePathLengthMismatch, MerkleNothingToProve, MerkleIndexOrHeightMismatch} from "../../common/L1ContractErrors.sol";
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    library Merkle {
        using UncheckedMath for uint256;
        /// @dev Calculate Merkle root by the provided Merkle proof.
        /// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack
        /// however, for chains settling on GW the proof includes the GW proof, so the path increases. See Mailbox for more details.
        /// @param _path Merkle path from the leaf to the root
        /// @param _index Leaf index in the tree
        /// @param _itemHash Hash of leaf content
        /// @return The Merkle root
        function calculateRoot(
            bytes32[] calldata _path,
            uint256 _index,
            bytes32 _itemHash
        ) internal pure returns (bytes32) {
            uint256 pathLength = _path.length;
            _validatePathLengthForSingleProof(_index, pathLength);
            bytes32 currentHash = _itemHash;
            for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
                currentHash = (_index % 2 == 0)
                    ? efficientHash(currentHash, _path[i])
                    : efficientHash(_path[i], currentHash);
                _index /= 2;
            }
            return currentHash;
        }
        /// @dev Calculate Merkle root by the provided Merkle proof.
        /// @dev NOTE: When using this function, check that the _path length is appropriate to prevent shorter/longer paths attack
        /// @param _path Merkle path from the leaf to the root
        /// @param _index Leaf index in the tree.
        /// @dev NOTE the tree can be joined. In this case the second tree's leaves indexes increase by the number of leaves in the first tree.
        /// @param _itemHash Hash of leaf content
        /// @return The Merkle root
        function calculateRootMemory(
            bytes32[] memory _path,
            uint256 _index,
            bytes32 _itemHash
        ) internal pure returns (bytes32) {
            uint256 pathLength = _path.length;
            _validatePathLengthForSingleProof(_index, pathLength);
            bytes32 currentHash = _itemHash;
            for (uint256 i; i < pathLength; i = i.uncheckedInc()) {
                currentHash = (_index % 2 == 0)
                    ? efficientHash(currentHash, _path[i])
                    : efficientHash(_path[i], currentHash);
                _index /= 2;
            }
            return currentHash;
        }
        /// @dev Calculate Merkle root by the provided Merkle proof for a range of elements
        /// NOTE: When using this function, check that the _startPath and _endPath lengths are equal to the tree height to prevent shorter/longer paths attack
        /// @param _startPath Merkle path from the first element of the range to the root
        /// @param _endPath Merkle path from the last element of the range to the root
        /// @param _startIndex Index of the first element of the range in the tree
        /// @param _itemHashes Hashes of the elements in the range
        /// @return The Merkle root
        function calculateRootPaths(
            bytes32[] memory _startPath,
            bytes32[] memory _endPath,
            uint256 _startIndex,
            bytes32[] memory _itemHashes
        ) internal pure returns (bytes32) {
            uint256 pathLength = _startPath.length;
            if (pathLength != _endPath.length) {
                revert MerklePathLengthMismatch(pathLength, _endPath.length);
            }
            if (pathLength >= 256) {
                revert MerklePathOutOfBounds();
            }
            uint256 levelLen = _itemHashes.length;
            // Edge case: we want to be able to prove an element in a single-node tree.
            if (pathLength == 0 && (_startIndex != 0 || levelLen != 1)) {
                revert MerklePathEmpty();
            }
            if (levelLen == 0) {
                revert MerkleNothingToProve();
            }
            if (_startIndex + levelLen > (1 << pathLength)) {
                revert MerkleIndexOrHeightMismatch();
            }
            bytes32[] memory itemHashes = _itemHashes;
            for (uint256 level; level < pathLength; level = level.uncheckedInc()) {
                uint256 parity = _startIndex % 2;
                // We get an extra element on the next level if on the current level elements either
                // start on an odd index (`parity == 1`) or end on an even index (`levelLen % 2 == 1`)
                uint256 nextLevelLen = levelLen / 2 + (parity | (levelLen % 2));
                for (uint256 i; i < nextLevelLen; i = i.uncheckedInc()) {
                    bytes32 lhs = (i == 0 && parity == 1) ? _startPath[level] : itemHashes[2 * i - parity];
                    bytes32 rhs = (i == nextLevelLen - 1 && (levelLen - parity) % 2 == 1)
                        ? _endPath[level]
                        : itemHashes[2 * i + 1 - parity];
                    itemHashes[i] = efficientHash(lhs, rhs);
                }
                levelLen = nextLevelLen;
                _startIndex /= 2;
            }
            return itemHashes[0];
        }
        /// @dev Keccak hash of the concatenation of two 32-byte words
        function efficientHash(bytes32 _lhs, bytes32 _rhs) internal pure returns (bytes32 result) {
            assembly {
                mstore(0x00, _lhs)
                mstore(0x20, _rhs)
                result := keccak256(0x00, 0x40)
            }
        }
        function _validatePathLengthForSingleProof(uint256 _index, uint256 _pathLength) private pure {
            if (_pathLength >= 256) {
                revert MerklePathOutOfBounds();
            }
            if (_index >= (1 << _pathLength)) {
                revert MerkleIndexOutOfBounds();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {QueueIsEmpty} from "../../common/L1ContractErrors.sol";
    /// @notice The structure that contains meta information of the L2 transaction that was requested from L1
    /// @dev The weird size of fields was selected specifically to minimize the structure storage size
    /// @param canonicalTxHash Hashed L2 transaction data that is needed to process it
    /// @param expirationTimestamp Expiration timestamp for this request (must be satisfied before)
    /// @param layer2Tip Additional payment to the validator as an incentive to perform the operation
    struct PriorityOperation {
        bytes32 canonicalTxHash;
        uint64 expirationTimestamp;
        uint192 layer2Tip;
    }
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @dev The library provides the API to interact with the priority queue container
    /// @dev Order of processing operations from queue - FIFO (Fist in - first out)
    library PriorityQueue {
        using PriorityQueue for Queue;
        /// @notice Container that stores priority operations
        /// @param data The inner mapping that saves priority operation by its index
        /// @param head The pointer to the first unprocessed priority operation, equal to the tail if the queue is empty
        /// @param tail The pointer to the free slot
        struct Queue {
            mapping(uint256 priorityOpId => PriorityOperation priorityOp) data;
            uint256 tail;
            uint256 head;
        }
        /// @notice Returns zero if and only if no operations were processed from the queue
        /// @return Index of the oldest priority operation that wasn't processed yet
        function getFirstUnprocessedPriorityTx(Queue storage _queue) internal view returns (uint256) {
            return _queue.head;
        }
        /// @return The total number of priority operations that were added to the priority queue, including all processed ones
        function getTotalPriorityTxs(Queue storage _queue) internal view returns (uint256) {
            return _queue.tail;
        }
        /// @return The total number of unprocessed priority operations in a priority queue
        function getSize(Queue storage _queue) internal view returns (uint256) {
            return uint256(_queue.tail - _queue.head);
        }
        /// @return Whether the priority queue contains no operations
        function isEmpty(Queue storage _queue) internal view returns (bool) {
            return _queue.tail == _queue.head;
        }
        /// @notice Add the priority operation to the end of the priority queue
        function pushBack(Queue storage _queue, PriorityOperation memory _operation) internal {
            // Save value into the stack to avoid double reading from the storage
            uint256 tail = _queue.tail;
            _queue.data[tail] = _operation;
            _queue.tail = tail + 1;
        }
        /// @return The first unprocessed priority operation from the queue
        function front(Queue storage _queue) internal view returns (PriorityOperation memory) {
            // priority queue is empty
            if (_queue.isEmpty()) {
                revert QueueIsEmpty();
            }
            return _queue.data[_queue.head];
        }
        /// @notice Remove the first unprocessed priority operation from the queue
        /// @return priorityOperation that was popped from the priority queue
        function popFront(Queue storage _queue) internal returns (PriorityOperation memory priorityOperation) {
            // priority queue is empty
            if (_queue.isEmpty()) {
                revert QueueIsEmpty();
            }
            // Save value into the stack to avoid double reading from the storage
            uint256 head = _queue.head;
            priorityOperation = _queue.data[head];
            delete _queue.data[head];
            _queue.head = head + 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the zkSync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {DynamicIncrementalMerkle} from "../../common/libraries/DynamicIncrementalMerkle.sol";
    import {Merkle} from "../../common/libraries/Merkle.sol";
    import {PriorityTreeCommitment} from "../../common/Config.sol";
    import {NotHistoricalRoot, InvalidCommitment, InvalidStartIndex, InvalidUnprocessedIndex, InvalidNextLeafIndex} from "../L1StateTransitionErrors.sol";
    struct PriorityOpsBatchInfo {
        bytes32[] leftPath;
        bytes32[] rightPath;
        bytes32[] itemHashes;
    }
    bytes32 constant ZERO_LEAF_HASH = keccak256("");
    library PriorityTree {
        using PriorityTree for Tree;
        using DynamicIncrementalMerkle for DynamicIncrementalMerkle.Bytes32PushTree;
        struct Tree {
            uint256 startIndex; // priority tree started accepting priority ops from this index
            uint256 unprocessedIndex; // relative to `startIndex`
            mapping(bytes32 => bool) historicalRoots;
            DynamicIncrementalMerkle.Bytes32PushTree tree;
        }
        /// @notice Returns zero if and only if no operations were processed from the tree
        /// @return Index of the oldest priority operation that wasn't processed yet
        function getFirstUnprocessedPriorityTx(Tree storage _tree) internal view returns (uint256) {
            return _tree.startIndex + _tree.unprocessedIndex;
        }
        /// @return The total number of priority operations that were added to the priority queue, including all processed ones
        function getTotalPriorityTxs(Tree storage _tree) internal view returns (uint256) {
            return _tree.startIndex + _tree.tree._nextLeafIndex;
        }
        /// @return The total number of unprocessed priority operations in a priority queue
        function getSize(Tree storage _tree) internal view returns (uint256) {
            return _tree.tree._nextLeafIndex - _tree.unprocessedIndex;
        }
        /// @notice Add the priority operation to the end of the priority queue
        function push(Tree storage _tree, bytes32 _hash) internal {
            (, bytes32 newRoot) = _tree.tree.push(_hash);
            _tree.historicalRoots[newRoot] = true;
        }
        /// @notice Set up the tree
        function setup(Tree storage _tree, uint256 _startIndex) internal {
            bytes32 initialRoot = _tree.tree.setup(ZERO_LEAF_HASH);
            _tree.historicalRoots[initialRoot] = true;
            _tree.startIndex = _startIndex;
        }
        /// @return Returns the tree root.
        function getRoot(Tree storage _tree) internal view returns (bytes32) {
            return _tree.tree.root();
        }
        /// @param _root The root to check.
        /// @return Returns true if the root is a historical root.
        function isHistoricalRoot(Tree storage _tree, bytes32 _root) internal view returns (bool) {
            return _tree.historicalRoots[_root];
        }
        /// @notice Process the priority operations of a batch.
        /// @dev Note, that the function below only checks that a certain segment of items is present in the tree.
        /// It does not check that e.g. there are no zero items inside the provided `itemHashes`, so in theory proofs
        /// that include non-existing priority operations could be created. This function relies on the fact
        /// that the `itemHashes` of `_priorityOpsData` are hashes of valid priority transactions.
        /// This fact is ensured by the fact the rolling hash of those is sent to the Executor by the bootloader
        /// and so assuming that zero knowledge proofs are correct, so is the structure of the `itemHashes`.
        function processBatch(Tree storage _tree, PriorityOpsBatchInfo memory _priorityOpsData) internal {
            if (_priorityOpsData.itemHashes.length > 0) {
                bytes32 expectedRoot = Merkle.calculateRootPaths(
                    _priorityOpsData.leftPath,
                    _priorityOpsData.rightPath,
                    _tree.unprocessedIndex,
                    _priorityOpsData.itemHashes
                );
                if (!_tree.historicalRoots[expectedRoot]) {
                    revert NotHistoricalRoot();
                }
                _tree.unprocessedIndex += _priorityOpsData.itemHashes.length;
            }
        }
        /// @notice Allows to skip a certain number of operations.
        /// @param _lastUnprocessed The new expected id of the unprocessed transaction.
        /// @dev It is used when the corresponding transactions have been processed by priority queue.
        function skipUntil(Tree storage _tree, uint256 _lastUnprocessed) internal {
            if (_tree.startIndex > _lastUnprocessed) {
                // Nothing to do, return
                return;
            }
            uint256 newUnprocessedIndex = _lastUnprocessed - _tree.startIndex;
            if (newUnprocessedIndex <= _tree.unprocessedIndex) {
                // These transactions were already processed, skip.
                return;
            }
            _tree.unprocessedIndex = newUnprocessedIndex;
        }
        /// @notice Initialize a chain from a commitment.
        function initFromCommitment(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal {
            uint256 height = _commitment.sides.length; // Height, including the root node.
            if (height == 0) {
                revert InvalidCommitment();
            }
            _tree.startIndex = _commitment.startIndex;
            _tree.unprocessedIndex = _commitment.unprocessedIndex;
            _tree.tree._nextLeafIndex = _commitment.nextLeafIndex;
            _tree.tree._sides = _commitment.sides;
            bytes32 zero = ZERO_LEAF_HASH;
            _tree.tree._zeros = new bytes32[](height);
            for (uint256 i; i < height; ++i) {
                _tree.tree._zeros[i] = zero;
                zero = Merkle.efficientHash(zero, zero);
            }
            _tree.historicalRoots[_tree.tree.root()] = true;
        }
        /// @notice Reinitialize the tree from a commitment on L1.
        function l1Reinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal {
            if (_tree.startIndex != _commitment.startIndex) {
                revert InvalidStartIndex(_tree.startIndex, _commitment.startIndex);
            }
            if (_tree.unprocessedIndex > _commitment.unprocessedIndex) {
                revert InvalidUnprocessedIndex(_tree.unprocessedIndex, _commitment.unprocessedIndex);
            }
            if (_tree.tree._nextLeafIndex < _commitment.nextLeafIndex) {
                revert InvalidNextLeafIndex(_tree.tree._nextLeafIndex, _commitment.nextLeafIndex);
            }
            _tree.unprocessedIndex = _commitment.unprocessedIndex;
        }
        /// @notice Reinitialize the tree from a commitment on GW.
        function checkGWReinit(Tree storage _tree, PriorityTreeCommitment memory _commitment) internal view {
            if (_tree.startIndex != _commitment.startIndex) {
                revert InvalidStartIndex(_tree.startIndex, _commitment.startIndex);
            }
            if (_tree.unprocessedIndex > _commitment.unprocessedIndex) {
                revert InvalidUnprocessedIndex(_tree.unprocessedIndex, _commitment.unprocessedIndex);
            }
            if (_tree.tree._nextLeafIndex > _commitment.nextLeafIndex) {
                revert InvalidNextLeafIndex(_tree.tree._nextLeafIndex, _commitment.nextLeafIndex);
            }
        }
        /// @notice Returns the commitment to the priority tree.
        function getCommitment(Tree storage _tree) internal view returns (PriorityTreeCommitment memory commitment) {
            commitment.nextLeafIndex = _tree.tree._nextLeafIndex;
            commitment.startIndex = _tree.startIndex;
            commitment.unprocessedIndex = _tree.unprocessedIndex;
            commitment.sides = _tree.tree._sides;
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {Math} from "@openzeppelin/contracts-v4/utils/math/Math.sol";
    import {L2CanonicalTransaction} from "../../common/Messaging.sol";
    import {TX_SLOT_OVERHEAD_L2_GAS, MEMORY_OVERHEAD_GAS, L1_TX_INTRINSIC_L2_GAS, L1_TX_DELTA_544_ENCODING_BYTES, L1_TX_DELTA_FACTORY_DEPS_L2_GAS, L1_TX_MIN_L2_GAS_BASE, L1_TX_INTRINSIC_PUBDATA, L1_TX_DELTA_FACTORY_DEPS_PUBDATA} from "../../common/Config.sol";
    import {TooMuchGas, InvalidUpgradeTxn, UpgradeTxVerifyParam, PubdataGreaterThanLimit, ValidateTxnNotEnoughGas, TxnBodyGasLimitNotEnoughGas} from "../../common/L1ContractErrors.sol";
    /// @title ZKsync Library for validating L1 -> L2 transactions
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    library TransactionValidator {
        /// @dev Used to validate key properties of an L1->L2 transaction
        /// @param _transaction The transaction to validate
        /// @param _encoded The abi encoded bytes of the transaction
        /// @param _priorityTxMaxGasLimit The max gas limit, generally provided from Storage.sol
        /// @param _priorityTxMaxPubdata The maximal amount of pubdata that a single L1->L2 transaction can emit
        function validateL1ToL2Transaction(
            L2CanonicalTransaction memory _transaction,
            bytes memory _encoded,
            uint256 _priorityTxMaxGasLimit,
            uint256 _priorityTxMaxPubdata
        ) internal pure {
            uint256 l2GasForTxBody = getTransactionBodyGasLimit(_transaction.gasLimit, _encoded.length);
            // Ensuring that the transaction is provable
            if (l2GasForTxBody > _priorityTxMaxGasLimit) {
                revert TooMuchGas();
            }
            // Ensuring that the transaction cannot output more pubdata than is processable
            if (l2GasForTxBody / _transaction.gasPerPubdataByteLimit > _priorityTxMaxPubdata) {
                revert PubdataGreaterThanLimit(_priorityTxMaxPubdata, l2GasForTxBody / _transaction.gasPerPubdataByteLimit);
            }
            // Ensuring that the transaction covers the minimal costs for its processing:
            // hashing its content, publishing the factory dependencies, etc.
            if (
                getMinimalPriorityTransactionGasLimit(
                    _encoded.length,
                    _transaction.factoryDeps.length,
                    _transaction.gasPerPubdataByteLimit
                ) > l2GasForTxBody
            ) {
                revert ValidateTxnNotEnoughGas();
            }
        }
        /// @dev Used to validate upgrade transactions
        /// @param _transaction The transaction to validate
        function validateUpgradeTransaction(L2CanonicalTransaction memory _transaction) internal pure {
            // Restrict from to be within system contract range (0...2^16 - 1)
            if (_transaction.from > type(uint16).max) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.From);
            }
            if (_transaction.to > type(uint160).max) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.To);
            }
            if (_transaction.paymaster != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Paymaster);
            }
            if (_transaction.value != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Value);
            }
            if (_transaction.maxFeePerGas != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.MaxFeePerGas);
            }
            if (_transaction.maxPriorityFeePerGas != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.MaxPriorityFeePerGas);
            }
            if (_transaction.reserved[0] != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved0);
            }
            if (_transaction.reserved[1] > type(uint160).max) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved1);
            }
            if (_transaction.reserved[2] != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved2);
            }
            if (_transaction.reserved[3] != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Reserved3);
            }
            if (_transaction.signature.length != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.Signature);
            }
            if (_transaction.paymasterInput.length != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.PaymasterInput);
            }
            if (_transaction.reservedDynamic.length != 0) {
                revert InvalidUpgradeTxn(UpgradeTxVerifyParam.ReservedDynamic);
            }
        }
        /// @dev Calculates the approximate minimum gas limit required for executing a priority transaction.
        /// @param _encodingLength The length of the priority transaction encoding in bytes.
        /// @param _numberOfFactoryDependencies The number of new factory dependencies that will be added.
        /// @param _l2GasPricePerPubdata The L2 gas price for publishing the priority transaction on L2.
        /// @return The minimum gas limit required to execute the priority transaction.
        /// Note: The calculation includes the main cost of the priority transaction, however, in reality, the operator can spend a little more gas on overheads.
        function getMinimalPriorityTransactionGasLimit(
            uint256 _encodingLength,
            uint256 _numberOfFactoryDependencies,
            uint256 _l2GasPricePerPubdata
        ) internal pure returns (uint256) {
            uint256 costForComputation;
            {
                // Adding the intrinsic cost for the transaction, i.e. auxiliary prices which cannot be easily accounted for
                costForComputation = L1_TX_INTRINSIC_L2_GAS;
                // Taking into account the hashing costs that depend on the length of the transaction
                // Note that L1_TX_DELTA_544_ENCODING_BYTES is the delta in the price for every 544 bytes of
                // the transaction's encoding. It is taken as LCM between 136 and 32 (the length for each keccak256 round
                // and the size of each new encoding word).
                costForComputation += Math.ceilDiv(_encodingLength * L1_TX_DELTA_544_ENCODING_BYTES, 544);
                // Taking into the account the additional costs of providing new factory dependencies
                costForComputation += _numberOfFactoryDependencies * L1_TX_DELTA_FACTORY_DEPS_L2_GAS;
                // There is a minimal amount of computational L2 gas that the transaction should cover
                costForComputation = Math.max(costForComputation, L1_TX_MIN_L2_GAS_BASE);
            }
            uint256 costForPubdata = 0;
            {
                // Adding the intrinsic cost for the transaction, i.e. auxiliary prices which cannot be easily accounted for
                costForPubdata = L1_TX_INTRINSIC_PUBDATA * _l2GasPricePerPubdata;
                // Taking into the account the additional costs of providing new factory dependencies
                costForPubdata += _numberOfFactoryDependencies * L1_TX_DELTA_FACTORY_DEPS_PUBDATA * _l2GasPricePerPubdata;
            }
            return costForComputation + costForPubdata;
        }
        /// @notice Based on the full L2 gas limit (that includes the batch overhead) and other
        /// properties of the transaction, returns the l2GasLimit for the body of the transaction (the actual execution).
        /// @param _totalGasLimit The L2 gas limit that includes both the overhead for processing the batch
        /// and the L2 gas needed to process the transaction itself (i.e. the actual l2GasLimit that will be used for the transaction).
        /// @param _encodingLength The length of the ABI-encoding of the transaction.
        function getTransactionBodyGasLimit(
            uint256 _totalGasLimit,
            uint256 _encodingLength
        ) internal pure returns (uint256 txBodyGasLimit) {
            uint256 overhead = getOverheadForTransaction(_encodingLength);
            // provided gas limit doesn't cover transaction overhead
            if (_totalGasLimit < overhead) {
                revert TxnBodyGasLimitNotEnoughGas();
            }
            unchecked {
                // We enforce the fact that `_totalGasLimit >= overhead` explicitly above.
                txBodyGasLimit = _totalGasLimit - overhead;
            }
        }
        /// @notice Based on the total L2 gas limit and several other parameters of the transaction
        /// returns the part of the L2 gas that will be spent on the batch's overhead.
        /// @dev The details of how this function works can be checked in the documentation
        /// of the fee model of ZKsync. The appropriate comments are also present
        /// in the Rust implementation description of function `get_maximal_allowed_overhead`.
        /// @param _encodingLength The length of the binary encoding of the transaction in bytes
        function getOverheadForTransaction(
            uint256 _encodingLength
        ) internal pure returns (uint256 batchOverheadForTransaction) {
            // The overhead from taking up the transaction's slot
            batchOverheadForTransaction = TX_SLOT_OVERHEAD_L2_GAS;
            // The overhead for occupying the bootloader memory can be derived from encoded_len
            uint256 overheadForLength = MEMORY_OVERHEAD_GAS * _encodingLength;
            batchOverheadForTransaction = Math.max(batchOverheadForTransaction, overheadForLength);
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @dev The enum that represents the transaction execution status
    /// @param Failure The transaction execution failed
    /// @param Success The transaction execution succeeded
    enum TxStatus {
        Failure,
        Success
    }
    /// @dev The log passed from L2
    /// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter
    /// All other values are not used but are reserved for the future
    /// @param isService A boolean flag that is part of the log along with `key`, `value`, and `sender` address.
    /// This field is required formally but does not have any special meaning
    /// @param txNumberInBatch The L2 transaction number in a Batch, in which the log was sent
    /// @param sender The L2 address which sent the log
    /// @param key The 32 bytes of information that was sent in the log
    /// @param value The 32 bytes of information that was sent in the log
    // Both `key` and `value` are arbitrary 32-bytes selected by the log sender
    struct L2Log {
        uint8 l2ShardId;
        bool isService;
        uint16 txNumberInBatch;
        address sender;
        bytes32 key;
        bytes32 value;
    }
    /// @dev An arbitrary length message passed from L2
    /// @notice Under the hood it is `L2Log` sent from the special system L2 contract
    /// @param txNumberInBatch The L2 transaction number in a Batch, in which the message was sent
    /// @param sender The address of the L2 account from which the message was passed
    /// @param data An arbitrary length message
    struct L2Message {
        uint16 txNumberInBatch;
        address sender;
        bytes data;
    }
    /// @dev Internal structure that contains the parameters for the writePriorityOp
    /// internal function.
    /// @param txId The id of the priority transaction.
    /// @param l2GasPrice The gas price for the l2 priority operation.
    /// @param expirationTimestamp The timestamp by which the priority operation must be processed by the operator.
    /// @param request The external calldata request for the priority operation.
    struct WritePriorityOpParams {
        uint256 txId;
        uint256 l2GasPrice;
        uint64 expirationTimestamp;
        BridgehubL2TransactionRequest request;
    }
    /// @dev Structure that includes all fields of the L2 transaction
    /// @dev The hash of this structure is the "canonical L2 transaction hash" and can
    /// be used as a unique identifier of a tx
    /// @param txType The tx type number, depending on which the L2 transaction can be
    /// interpreted differently
    /// @param from The sender's address. `uint256` type for possible address format changes
    /// and maintaining backward compatibility
    /// @param to The recipient's address. `uint256` type for possible address format changes
    /// and maintaining backward compatibility
    /// @param gasLimit The L2 gas limit for L2 transaction. Analog to the `gasLimit` on an
    /// L1 transactions
    /// @param gasPerPubdataByteLimit Maximum number of L2 gas that will cost one byte of pubdata
    /// (every piece of data that will be stored on L1 as calldata)
    /// @param maxFeePerGas The absolute maximum sender willing to pay per unit of L2 gas to get
    /// the transaction included in a Batch. Analog to the EIP-1559 `maxFeePerGas` on an L1 transactions
    /// @param maxPriorityFeePerGas The additional fee that is paid directly to the validator
    /// to incentivize them to include the transaction in a Batch. Analog to the EIP-1559
    /// `maxPriorityFeePerGas` on an L1 transactions
    /// @param paymaster The address of the EIP-4337 paymaster, that will pay fees for the
    /// transaction. `uint256` type for possible address format changes and maintaining backward compatibility
    /// @param nonce The nonce of the transaction. For L1->L2 transactions it is the priority
    /// operation Id
    /// @param value The value to pass with the transaction
    /// @param reserved The fixed-length fields for usage in a future extension of transaction
    /// formats
    /// @param data The calldata that is transmitted for the transaction call
    /// @param signature An abstract set of bytes that are used for transaction authorization
    /// @param factoryDeps The set of L2 bytecode hashes whose preimages were shown on L1
    /// @param paymasterInput The arbitrary-length data that is used as a calldata to the paymaster pre-call
    /// @param reservedDynamic The arbitrary-length field for usage in a future extension of transaction formats
    struct L2CanonicalTransaction {
        uint256 txType;
        uint256 from;
        uint256 to;
        uint256 gasLimit;
        uint256 gasPerPubdataByteLimit;
        uint256 maxFeePerGas;
        uint256 maxPriorityFeePerGas;
        uint256 paymaster;
        uint256 nonce;
        uint256 value;
        // In the future, we might want to add some
        // new fields to the struct. The `txData` struct
        // is to be passed to account and any changes to its structure
        // would mean a breaking change to these accounts. To prevent this,
        // we should keep some fields as "reserved"
        // It is also recommended that their length is fixed, since
        // it would allow easier proof integration (in case we will need
        // some special circuit for preprocessing transactions)
        uint256[4] reserved;
        bytes data;
        bytes signature;
        uint256[] factoryDeps;
        bytes paymasterInput;
        // Reserved dynamic type for the future use-case. Using it should be avoided,
        // But it is still here, just in case we want to enable some additional functionality
        bytes reservedDynamic;
    }
    /// @param sender The sender's address.
    /// @param contractAddressL2 The address of the contract on L2 to call.
    /// @param valueToMint The amount of base token that should be minted on L2 as the result of this transaction.
    /// @param l2Value The msg.value of the L2 transaction.
    /// @param l2Calldata The calldata for the L2 transaction.
    /// @param l2GasLimit The limit of the L2 gas for the L2 transaction
    /// @param l2GasPerPubdataByteLimit The price for a single pubdata byte in L2 gas.
    /// @param factoryDeps The array of L2 bytecodes that the tx depends on.
    /// @param refundRecipient The recipient of the refund for the transaction on L2. If the transaction fails, then
    /// this address will receive the `l2Value`.
    // solhint-disable-next-line gas-struct-packing
    struct BridgehubL2TransactionRequest {
        address sender;
        address contractL2;
        uint256 mintValue;
        uint256 l2Value;
        bytes l2Calldata;
        uint256 l2GasLimit;
        uint256 l2GasPerPubdataByteLimit;
        bytes[] factoryDeps;
        address refundRecipient;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    bytes32 constant BATCH_LEAF_PADDING = keccak256("zkSync:BatchLeaf");
    bytes32 constant CHAIN_ID_LEAF_PADDING = keccak256("zkSync:ChainIdLeaf");
    library MessageHashing {
        /// @dev Returns the leaf hash for a chain with batch number and batch root.
        /// @param batchRoot The root hash of the batch.
        /// @param batchNumber The number of the batch.
        function batchLeafHash(bytes32 batchRoot, uint256 batchNumber) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(BATCH_LEAF_PADDING, batchRoot, batchNumber));
        }
        /// @dev Returns the leaf hash for a chain with chain root and chain id.
        /// @param chainIdRoot The root hash of the chain.
        /// @param chainId The id of the chain.
        function chainIdLeafHash(bytes32 chainIdRoot, uint256 chainId) internal pure returns (bytes32) {
            return keccak256(abi.encodePacked(CHAIN_ID_LEAF_PADDING, chainIdRoot, chainId));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IVerifier, VerifierParams} from "../chain-interfaces/IVerifier.sol";
    // import {IChainTypeManager} from "../IChainTypeManager.sol";
    import {PriorityQueue} from "../../state-transition/libraries/PriorityQueue.sol";
    import {PriorityTree} from "../../state-transition/libraries/PriorityTree.sol";
    /// @notice Indicates whether an upgrade is initiated and if yes what type
    /// @param None Upgrade is NOT initiated
    /// @param Transparent Fully transparent upgrade is initiated, upgrade data is publicly known
    /// @param Shadow Shadow upgrade is initiated, upgrade data is hidden
    enum UpgradeState {
        None,
        Transparent,
        Shadow
    }
    /// @dev Logically separated part of the storage structure, which is responsible for everything related to proxy
    /// upgrades and diamond cuts
    /// @param proposedUpgradeHash The hash of the current upgrade proposal, zero if there is no active proposal
    /// @param state Indicates whether an upgrade is initiated and if yes what type
    /// @param securityCouncil Address which has the permission to approve instant upgrades (expected to be a Gnosis
    /// multisig)
    /// @param approvedBySecurityCouncil Indicates whether the security council has approved the upgrade
    /// @param proposedUpgradeTimestamp The timestamp when the upgrade was proposed, zero if there are no active proposals
    /// @param currentProposalId The serial number of proposed upgrades, increments when proposing a new one
    struct UpgradeStorage {
        bytes32 proposedUpgradeHash;
        UpgradeState state;
        address securityCouncil;
        bool approvedBySecurityCouncil;
        uint40 proposedUpgradeTimestamp;
        uint40 currentProposalId;
    }
    /// @notice The struct that describes whether users will be charged for pubdata for L1->L2 transactions.
    /// @param Rollup The users are charged for pubdata & it is priced based on the gas price on Ethereum.
    /// @param Validium The pubdata is considered free with regard to the L1 gas price.
    enum PubdataPricingMode {
        Rollup,
        Validium
    }
    /// @notice The fee params for L1->L2 transactions for the network.
    /// @param pubdataPricingMode How the users will charged for pubdata in L1->L2 transactions.
    /// @param batchOverheadL1Gas The amount of L1 gas required to process the batch (except for the calldata).
    /// @param maxPubdataPerBatch The maximal number of pubdata that can be emitted per batch.
    /// @param priorityTxMaxPubdata The maximal amount of pubdata a priority transaction is allowed to publish.
    /// It can be slightly less than maxPubdataPerBatch in order to have some margin for the bootloader execution.
    /// @param minimalL2GasPrice The minimal L2 gas price to be used by L1->L2 transactions. It should represent
    /// the price that a single unit of compute costs.
    struct FeeParams {
        PubdataPricingMode pubdataPricingMode;
        uint32 batchOverheadL1Gas;
        uint32 maxPubdataPerBatch;
        uint32 maxL2GasPerBatch;
        uint32 priorityTxMaxPubdata;
        uint64 minimalL2GasPrice;
    }
    /// @dev storing all storage variables for ZK chain diamond facets
    /// NOTE: It is used in a proxy, so it is possible to add new variables to the end
    /// but NOT to modify already existing variables or change their order.
    /// NOTE: variables prefixed with '__DEPRECATED_' are deprecated and shouldn't be used.
    /// Their presence is maintained for compatibility and to prevent storage collision.
    // solhint-disable-next-line gas-struct-packing
    struct ZKChainStorage {
        /// @dev Storage of variables needed for deprecated diamond cut facet
        uint256[7] __DEPRECATED_diamondCutStorage;
        /// @notice Address which will exercise critical changes to the Diamond Proxy (upgrades, freezing & unfreezing). Replaced by CTM
        address __DEPRECATED_governor;
        /// @notice Address that the governor proposed as one that will replace it
        address __DEPRECATED_pendingGovernor;
        /// @notice List of permitted validators
        mapping(address validatorAddress => bool isValidator) validators;
        /// @dev Verifier contract. Used to verify aggregated proof for batches
        IVerifier verifier;
        /// @notice Total number of executed batches i.e. batches[totalBatchesExecuted] points at the latest executed batch
        /// (batch 0 is genesis)
        uint256 totalBatchesExecuted;
        /// @notice Total number of proved batches i.e. batches[totalBatchesProved] points at the latest proved batch
        uint256 totalBatchesVerified;
        /// @notice Total number of committed batches i.e. batches[totalBatchesCommitted] points at the latest committed
        /// batch
        uint256 totalBatchesCommitted;
        /// @dev Stored hashed StoredBatch for batch number
        mapping(uint256 batchNumber => bytes32 batchHash) storedBatchHashes;
        /// @dev Stored root hashes of L2 -> L1 logs
        mapping(uint256 batchNumber => bytes32 l2LogsRootHash) l2LogsRootHashes;
        /// @dev Container that stores transactions requested from L1
        PriorityQueue.Queue priorityQueue;
        /// @dev The smart contract that manages the list with permission to call contract functions
        address __DEPRECATED_allowList;
        VerifierParams __DEPRECATED_verifierParams;
        /// @notice Bytecode hash of bootloader program.
        /// @dev Used as an input to zkp-circuit.
        bytes32 l2BootloaderBytecodeHash;
        /// @notice Bytecode hash of default account (bytecode for EOA).
        /// @dev Used as an input to zkp-circuit.
        bytes32 l2DefaultAccountBytecodeHash;
        /// @dev Indicates that the porter may be touched on L2 transactions.
        /// @dev Used as an input to zkp-circuit.
        bool zkPorterIsAvailable;
        /// @dev The maximum number of the L2 gas that a user can request for L1 -> L2 transactions
        /// @dev This is the maximum number of L2 gas that is available for the "body" of the transaction, i.e.
        /// without overhead for proving the batch.
        uint256 priorityTxMaxGasLimit;
        /// @dev Storage of variables needed for upgrade facet
        UpgradeStorage __DEPRECATED_upgrades;
        /// @dev A mapping L2 batch number => message number => flag.
        /// @dev The L2 -> L1 log is sent for every withdrawal, so this mapping is serving as
        /// a flag to indicate that the message was already processed.
        /// @dev Used to indicate that eth withdrawal was already processed
        mapping(uint256 l2BatchNumber => mapping(uint256 l2ToL1MessageNumber => bool isFinalized)) isEthWithdrawalFinalized;
        /// @dev The most recent withdrawal time and amount reset
        uint256 __DEPRECATED_lastWithdrawalLimitReset;
        /// @dev The accumulated withdrawn amount during the withdrawal limit window
        uint256 __DEPRECATED_withdrawnAmountInWindow;
        /// @dev A mapping user address => the total deposited amount by the user
        mapping(address => uint256) __DEPRECATED_totalDepositedAmountPerUser;
        /// @dev Stores the protocol version. Note, that the protocol version may not only encompass changes to the
        /// smart contracts, but also to the node behavior.
        uint256 protocolVersion;
        /// @dev Hash of the system contract upgrade transaction. If 0, then no upgrade transaction needs to be done.
        bytes32 l2SystemContractsUpgradeTxHash;
        /// @dev Batch number where the upgrade transaction has happened. If 0, then no upgrade transaction has happened
        /// yet.
        uint256 l2SystemContractsUpgradeBatchNumber;
        /// @dev Address which will exercise non-critical changes to the Diamond Proxy (changing validator set & unfreezing)
        address admin;
        /// @notice Address that the admin proposed as one that will replace admin role
        address pendingAdmin;
        /// @dev Fee params used to derive gasPrice for the L1->L2 transactions. For L2 transactions,
        /// the bootloader gives enough freedom to the operator.
        /// @dev The value is only for the L1 deployment of the ZK Chain, since payment for all the priority transactions is
        /// charged at that level.
        FeeParams feeParams;
        /// @dev Address of the blob versioned hash getter smart contract used for EIP-4844 versioned hashes.
        /// @dev Used only for testing.
        address blobVersionedHashRetriever;
        /// @dev The chainId of the chain
        uint256 chainId;
        /// @dev The address of the bridgehub
        address bridgehub;
        /// @dev The address of the ChainTypeManager
        address chainTypeManager;
        /// @dev The address of the baseToken contract. Eth is address(1)
        address __DEPRECATED_baseToken;
        /// @dev The address of the baseTokenbridge. Eth also uses the shared bridge
        address __DEPRECATED_baseTokenBridge;
        /// @notice gasPriceMultiplier for each baseToken, so that each L1->L2 transaction pays for its transaction on the destination
        /// we multiply by the nominator, and divide by the denominator
        uint128 baseTokenGasPriceMultiplierNominator;
        uint128 baseTokenGasPriceMultiplierDenominator;
        /// @dev The optional address of the contract that has to be used for transaction filtering/whitelisting
        address transactionFilterer;
        /// @dev The address of the l1DAValidator contract.
        /// This contract is responsible for the verification of the correctness of the DA on L1.
        address l1DAValidator;
        /// @dev The address of the contract on L2 that is responsible for the data availability verification.
        /// This contract sends `l2DAValidatorOutputHash` to L1 via L2->L1 system log and it will routed to the `l1DAValidator` contract.
        address l2DAValidator;
        /// @dev the Asset Id of the baseToken
        bytes32 baseTokenAssetId;
        /// @dev If this ZKchain settles on this chain, then this is zero. Otherwise it is the address of the ZKchain that is a
        /// settlement layer for this ZKchain. (think about it as a 'forwarding' address for the chain that migrated away).
        address settlementLayer;
        /// @dev Priority tree, the new data structure for priority queue
        PriorityTree.Tree priorityTree;
        /// @dev Whether the chain is a permanent rollup. Note, that it only enforces the DA validator pair, but
        /// it does not enforce any other parameters, e.g. `pubdataPricingMode`
        bool isPermanentRollup;
        /// @notice Bytecode hash of evm emulator.
        /// @dev Used as an input to zkp-circuit.
        bytes32 l2EvmEmulatorBytecodeHash;
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /**
     * @author Matter Labs
     * @custom:security-contact [email protected]
     * @notice The library for unchecked math.
     */
    library UncheckedMath {
        function uncheckedInc(uint256 _number) internal pure returns (uint256) {
            unchecked {
                return _number + 1;
            }
        }
        function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) {
            unchecked {
                return _lhs + _rhs;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {BytecodeError, MalformedBytecode, LengthIsNotDivisibleBy32} from "../L1ContractErrors.sol";
    import {UncheckedMath} from "./UncheckedMath.sol";
    import {L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR} from "../L2ContractAddresses.sol";
    /**
     * @author Matter Labs
     * @custom:security-contact [email protected]
     * @notice Interface for the contract that is used to deploy contracts on L2.
     */
    interface IContractDeployer {
        /// @notice A struct that describes a forced deployment on an address.
        /// @param bytecodeHash The bytecode hash to put on an address.
        /// @param newAddress The address on which to deploy the bytecodehash to.
        /// @param callConstructor Whether to run the constructor on the force deployment.
        /// @param value The `msg.value` with which to initialize a contract.
        /// @param input The constructor calldata.
        struct ForceDeployment {
            bytes32 bytecodeHash;
            address newAddress;
            bool callConstructor;
            uint256 value;
            bytes input;
        }
        /// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses.
        /// @param _deployParams A set of parameters describing force deployment.
        function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external payable;
        /// @notice Creates a new contract at a determined address using the `CREATE2` salt on L2
        /// @param _salt a unique value to create the deterministic address of the new contract
        /// @param _bytecodeHash the bytecodehash of the new contract to be deployed
        /// @param _input the calldata to be sent to the constructor of the new contract
        function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external returns (address);
    }
    /**
     * @author Matter Labs
     * @custom:security-contact [email protected]
     * @notice Helper library for working with L2 contracts on L1.
     */
    library L2ContractHelper {
        using UncheckedMath for uint256;
        /// @dev The prefix used to create CREATE2 addresses.
        bytes32 private constant CREATE2_PREFIX = keccak256("zksyncCreate2");
        /// @dev Prefix used during derivation of account addresses using CREATE
        /// @dev keccak256("zksyncCreate")
        bytes32 private constant CREATE_PREFIX = 0x63bae3a9951d38e8a3fbb7b70909afc1200610fc5bc55ade242f815974674f23;
        /// @notice Sends L2 -> L1 arbitrary-long message through the system contract messenger.
        /// @param _message Data to be sent to L1.
        /// @return keccak256 hash of the sent message.
        function sendMessageToL1(bytes memory _message) internal returns (bytes32) {
            return L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR.sendToL1(_message);
        }
        /// @notice Validate the bytecode format and calculate its hash.
        /// @param _bytecode The bytecode to hash.
        /// @return hashedBytecode The 32-byte hash of the bytecode.
        /// Note: The function reverts the execution if the bytecode has non expected format:
        /// - Bytecode bytes length is not a multiple of 32
        /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
        /// - Bytecode words length is not odd
        function hashL2Bytecode(bytes memory _bytecode) internal pure returns (bytes32 hashedBytecode) {
            // Note that the length of the bytecode must be provided in 32-byte words.
            if (_bytecode.length % 32 != 0) {
                revert LengthIsNotDivisibleBy32(_bytecode.length);
            }
            uint256 bytecodeLenInWords = _bytecode.length / 32;
            // bytecode length must be less than 2^16 words
            if (bytecodeLenInWords >= 2 ** 16) {
                revert MalformedBytecode(BytecodeError.NumberOfWords);
            }
            // bytecode length in words must be odd
            if (bytecodeLenInWords % 2 == 0) {
                revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
            }
            hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
            // Setting the version of the hash
            hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
            // Setting the length
            hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
        }
        /// @notice Validate the bytecode format and calculate its hash.
        /// @param _bytecode The bytecode to hash.
        /// @return hashedBytecode The 32-byte hash of the bytecode.
        /// Note: The function reverts the execution if the bytecode has non expected format:
        /// - Bytecode bytes length is not a multiple of 32
        /// - Bytecode bytes length is not less than 2^21 bytes (2^16 words)
        /// - Bytecode words length is not odd
        function hashL2BytecodeCalldata(bytes calldata _bytecode) internal pure returns (bytes32 hashedBytecode) {
            // Note that the length of the bytecode must be provided in 32-byte words.
            if (_bytecode.length % 32 != 0) {
                revert LengthIsNotDivisibleBy32(_bytecode.length);
            }
            uint256 bytecodeLenInWords = _bytecode.length / 32;
            // bytecode length must be less than 2^16 words
            if (bytecodeLenInWords >= 2 ** 16) {
                revert MalformedBytecode(BytecodeError.NumberOfWords);
            }
            // bytecode length in words must be odd
            if (bytecodeLenInWords % 2 == 0) {
                revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
            }
            hashedBytecode = sha256(_bytecode) & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
            // Setting the version of the hash
            hashedBytecode = (hashedBytecode | bytes32(uint256(1 << 248)));
            // Setting the length
            hashedBytecode = hashedBytecode | bytes32(bytecodeLenInWords << 224);
        }
        /// @notice Validates the format of the given bytecode hash.
        /// @dev Due to the specification of the L2 bytecode hash, not every 32 bytes could be a legit bytecode hash.
        /// @dev The function reverts on invalid bytecode hash format.
        /// @param _bytecodeHash The hash of the bytecode to validate.
        function validateBytecodeHash(bytes32 _bytecodeHash) internal pure {
            uint8 version = uint8(_bytecodeHash[0]);
            // Incorrectly formatted bytecodeHash
            if (version != 1 || _bytecodeHash[1] != bytes1(0)) {
                revert MalformedBytecode(BytecodeError.Version);
            }
            // Code length in words must be odd
            if (bytecodeLen(_bytecodeHash) % 2 == 0) {
                revert MalformedBytecode(BytecodeError.WordsMustBeOdd);
            }
        }
        /// @notice Returns the length of the bytecode associated with the given hash.
        /// @param _bytecodeHash The hash of the bytecode.
        /// @return codeLengthInWords The length of the bytecode in words.
        function bytecodeLen(bytes32 _bytecodeHash) internal pure returns (uint256 codeLengthInWords) {
            codeLengthInWords = uint256(uint8(_bytecodeHash[2])) * 256 + uint256(uint8(_bytecodeHash[3]));
        }
        /// @notice Computes the create2 address for a Layer 2 contract.
        /// @param _sender The address of the sender.
        /// @param _salt The salt value to use in the create2 address computation.
        /// @param _bytecodeHash The contract bytecode hash.
        /// @param _constructorInputHash The hash of the constructor input data.
        /// @return The create2 address of the contract.
        /// NOTE: L2 create2 derivation is different from L1 derivation!
        function computeCreate2Address(
            address _sender,
            bytes32 _salt,
            bytes32 _bytecodeHash,
            bytes32 _constructorInputHash
        ) internal pure returns (address) {
            bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
            bytes32 data = keccak256(
                // solhint-disable-next-line func-named-parameters
                bytes.concat(CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash)
            );
            return address(uint160(uint256(data)));
        }
        /// @notice Calculates the address of a deployed contract via create
        /// @param _sender The account that deploys the contract.
        /// @param _senderNonce The deploy nonce of the sender's account.
        /// NOTE: L2 create derivation is different from L1 derivation!
        function computeCreateAddress(address _sender, uint256 _senderNonce) internal pure returns (address) {
            // No collision is possible with the Ethereum's CREATE, since
            // the prefix begins with 0x63....
            bytes32 hash = keccak256(
                bytes.concat(CREATE_PREFIX, bytes32(uint256(uint160(_sender))), bytes32(_senderNonce))
            );
            return address(uint160(uint256(hash)));
        }
        /// @notice Hashes the L2 bytecodes and returns them in the format in which they are processed by the bootloader
        function hashFactoryDeps(bytes[] memory _factoryDeps) internal pure returns (uint256[] memory hashedFactoryDeps) {
            uint256 factoryDepsLen = _factoryDeps.length;
            hashedFactoryDeps = new uint256[](factoryDepsLen);
            for (uint256 i = 0; i < factoryDepsLen; i = i.uncheckedInc()) {
                bytes32 hashedBytecode = hashL2Bytecode(_factoryDeps[i]);
                // Store the resulting hash sequentially in words.
                assembly {
                    mstore(add(hashedFactoryDeps, mul(add(i, 1), 32)), hashedBytecode)
                }
            }
        }
    }
    // SPDX-License-Identifier: Apache-2.0
    /*
     * Copyright 2019-2021, Offchain Labs, Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    library AddressAliasHelper {
        uint160 private constant offset = uint160(0x1111000000000000000000000000000000001111);
        /// @notice Utility function converts the address that submitted a tx
        /// to the inbox on L1 to the msg.sender viewed on L2
        /// @param l1Address the address in the L1 that triggered the tx to L2
        /// @return l2Address L2 address as viewed in msg.sender
        function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
            unchecked {
                l2Address = address(uint160(l1Address) + offset);
            }
        }
        /// @notice Utility function that converts the msg.sender viewed on L2 to the
        /// address that submitted a tx to the inbox on L1
        /// @param l2Address L2 address as viewed in msg.sender
        /// @return l1Address the address in the L1 that triggered the tx to L2
        function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) {
            unchecked {
                l1Address = address(uint160(l2Address) - offset);
            }
        }
        /// @notice Utility function used to calculate the correct refund recipient
        /// @param _refundRecipient the address that should receive the refund
        /// @param _originalCaller the address that triggered the tx to L2
        /// @return _recipient the corrected address that should receive the refund
        function actualRefundRecipient(
            address _refundRecipient,
            address _originalCaller
        ) internal view returns (address _recipient) {
            if (_refundRecipient == address(0)) {
                // If the `_refundRecipient` is not provided, we use the `_originalCaller` as the recipient.
                // solhint-disable avoid-tx-origin
                // slither-disable-next-line tx-origin
                _recipient = _originalCaller == tx.origin
                    ? _originalCaller
                    : AddressAliasHelper.applyL1ToL2Alias(_originalCaller);
                // solhint-enable avoid-tx-origin
            } else if (_refundRecipient.code.length > 0) {
                // If the `_refundRecipient` is a smart contract, we apply the L1 to L2 alias to prevent foot guns.
                _recipient = AddressAliasHelper.applyL1ToL2Alias(_refundRecipient);
            } else {
                _recipient = _refundRecipient;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {ZKChainStorage} from "../ZKChainStorage.sol";
    import {ReentrancyGuard} from "../../../common/ReentrancyGuard.sol";
    import {PriorityQueue} from "../../libraries/PriorityQueue.sol";
    import {PriorityTree} from "../../libraries/PriorityTree.sol";
    import {NotSettlementLayer} from "../../L1StateTransitionErrors.sol";
    import {Unauthorized} from "../../../common/L1ContractErrors.sol";
    /// @title Base contract containing functions accessible to the other facets.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    contract ZKChainBase is ReentrancyGuard {
        using PriorityQueue for PriorityQueue.Queue;
        using PriorityTree for PriorityTree.Tree;
        // slither-disable-next-line uninitialized-state
        ZKChainStorage internal s;
        /// @notice Checks that the message sender is an active admin
        modifier onlyAdmin() {
            if (msg.sender != s.admin) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        /// @notice Checks if validator is active
        modifier onlyValidator() {
            if (!s.validators[msg.sender]) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        modifier onlyChainTypeManager() {
            if (msg.sender != s.chainTypeManager) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        modifier onlyBridgehub() {
            if (msg.sender != s.bridgehub) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        modifier onlyAdminOrChainTypeManager() {
            if (msg.sender != s.admin && msg.sender != s.chainTypeManager) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        modifier onlyValidatorOrChainTypeManager() {
            if (!s.validators[msg.sender] && msg.sender != s.chainTypeManager) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        modifier onlySettlementLayer() {
            if (s.settlementLayer != address(0)) {
                revert NotSettlementLayer();
            }
            _;
        }
        modifier onlySelf() {
            if (msg.sender != address(this)) {
                revert Unauthorized(msg.sender);
            }
            _;
        }
        /// @notice Returns whether the priority queue is still active, i.e.
        /// the chain has not processed all transactions from it
        function _isPriorityQueueActive() internal view returns (bool) {
            return s.priorityQueue.getFirstUnprocessedPriorityTx() < s.priorityTree.startIndex;
        }
        /// @notice Ensures that the queue is deactivated. Should be invoked
        /// whenever the chain migrates to another settlement layer.
        function _forceDeactivateQueue() internal {
            // We double check whether it is still active mainly to prevent
            // overriding `tail`/`head` on L1 deployment.
            if (_isPriorityQueueActive()) {
                uint256 startIndex = s.priorityTree.startIndex;
                s.priorityQueue.head = startIndex;
                s.priorityQueue.tail = startIndex;
            }
        }
        function _getTotalPriorityTxs() internal view returns (uint256) {
            if (_isPriorityQueueActive()) {
                return s.priorityQueue.getTotalPriorityTxs();
            } else {
                return s.priorityTree.getTotalPriorityTxs();
            }
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @dev `keccak256("")`
    bytes32 constant EMPTY_STRING_KECCAK = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
    /// @dev Bytes in raw L2 log
    /// @dev Equal to the bytes size of the tuple - (uint8 ShardId, bool isService, uint16 txNumberInBatch, address sender,
    /// bytes32 key, bytes32 value)
    uint256 constant L2_TO_L1_LOG_SERIALIZE_SIZE = 88;
    /// @dev The maximum length of the bytes array with L2 -> L1 logs
    uint256 constant MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES = 4 + L2_TO_L1_LOG_SERIALIZE_SIZE * 512;
    /// @dev The value of default leaf hash for L2 -> L1 logs Merkle tree
    /// @dev An incomplete fixed-size tree is filled with this value to be a full binary tree
    /// @dev Actually equal to the `keccak256(new bytes(L2_TO_L1_LOG_SERIALIZE_SIZE))`
    bytes32 constant L2_L1_LOGS_TREE_DEFAULT_LEAF_HASH = 0x72abee45b59e344af8a6e520241c4744aff26ed411f4c4b00f8af09adada43ba;
    bytes32 constant DEFAULT_L2_LOGS_TREE_ROOT_HASH = bytes32(0);
    /// @dev Denotes the type of the ZKsync transaction that came from L1.
    uint256 constant PRIORITY_OPERATION_L2_TX_TYPE = 255;
    /// @dev Denotes the type of the ZKsync transaction that is used for system upgrades.
    uint256 constant SYSTEM_UPGRADE_L2_TX_TYPE = 254;
    /// @dev The maximal allowed difference between protocol minor versions in an upgrade. The 100 gap is needed
    /// in case a protocol version has been tested on testnet, but then not launched on mainnet, e.g.
    /// due to a bug found.
    /// We are allowed to jump at most 100 minor versions at a time. The major version is always expected to be 0.
    uint256 constant MAX_ALLOWED_MINOR_VERSION_DELTA = 100;
    /// @dev The amount of time in seconds the validator has to process the priority transaction
    /// NOTE: The constant is set to zero for the Alpha release period
    uint256 constant PRIORITY_EXPIRATION = 0 days;
    /// @dev Timestamp - seconds since unix epoch.
    uint256 constant COMMIT_TIMESTAMP_NOT_OLDER = 3 days;
    /// @dev Maximum available error between real commit batch timestamp and analog used in the verifier (in seconds)
    /// @dev Must be used cause miner's `block.timestamp` value can differ on some small value (as we know - 12 seconds)
    uint256 constant COMMIT_TIMESTAMP_APPROXIMATION_DELTA = 1 hours;
    /// @dev Shift to apply to verify public input before verifying.
    uint256 constant PUBLIC_INPUT_SHIFT = 32;
    /// @dev The maximum number of L2 gas that a user can request for an L2 transaction
    uint256 constant MAX_GAS_PER_TRANSACTION = 80_000_000;
    /// @dev Even though the price for 1 byte of pubdata is 16 L1 gas, we have a slightly increased
    /// value.
    uint256 constant L1_GAS_PER_PUBDATA_BYTE = 17;
    /// @dev The intrinsic cost of the L1->l2 transaction in computational L2 gas
    uint256 constant L1_TX_INTRINSIC_L2_GAS = 167_157;
    /// @dev The intrinsic cost of the L1->l2 transaction in pubdata
    uint256 constant L1_TX_INTRINSIC_PUBDATA = 88;
    /// @dev The minimal base price for L1 transaction
    uint256 constant L1_TX_MIN_L2_GAS_BASE = 173_484;
    /// @dev The number of L2 gas the transaction starts costing more with each 544 bytes of encoding
    uint256 constant L1_TX_DELTA_544_ENCODING_BYTES = 1656;
    /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
    uint256 constant L1_TX_DELTA_FACTORY_DEPS_L2_GAS = 2473;
    /// @dev The number of L2 gas an L1->L2 transaction gains with each new factory dependency
    uint256 constant L1_TX_DELTA_FACTORY_DEPS_PUBDATA = 64;
    /// @dev The number of pubdata an L1->L2 transaction requires with each new factory dependency
    uint256 constant MAX_NEW_FACTORY_DEPS = 64;
    /// @dev The L2 gasPricePerPubdata required to be used in bridges.
    uint256 constant REQUIRED_L2_GAS_PRICE_PER_PUBDATA = 800;
    /// @dev The mask which should be applied to the packed batch and L2 block timestamp in order
    /// to obtain the L2 block timestamp. Applying this mask is equivalent to calculating modulo 2**128
    uint256 constant PACKED_L2_BLOCK_TIMESTAMP_MASK = 0xffffffffffffffffffffffffffffffff;
    /// @dev Address of the point evaluation precompile used for EIP-4844 blob verification.
    address constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A);
    /// @dev The overhead for a transaction slot in L2 gas.
    /// It is roughly equal to 80kk/MAX_TRANSACTIONS_IN_BATCH, i.e. how many gas would an L1->L2 transaction
    /// need to pay to compensate for the batch being closed.
    /// @dev It is expected that the L1 contracts will enforce that the L2 gas price will be high enough to compensate
    /// the operator in case the batch is closed because of tx slots filling up.
    uint256 constant TX_SLOT_OVERHEAD_L2_GAS = 10000;
    /// @dev The overhead for each byte of the bootloader memory that the encoding of the transaction.
    /// It is roughly equal to 80kk/BOOTLOADER_MEMORY_FOR_TXS, i.e. how many gas would an L1->L2 transaction
    /// need to pay to compensate for the batch being closed.
    /// @dev It is expected that the L1 contracts will enforce that the L2 gas price will be high enough to compensate
    /// the operator in case the batch is closed because of the memory for transactions being filled up.
    uint256 constant MEMORY_OVERHEAD_GAS = 10;
    /// @dev The maximum gas limit for a priority transaction in L2.
    uint256 constant PRIORITY_TX_MAX_GAS_LIMIT = 72_000_000;
    /// @dev the address used to identify eth as the base token for chains.
    address constant ETH_TOKEN_ADDRESS = address(1);
    /// @dev the value returned in bridgehubDeposit in the TwoBridges function.
    bytes32 constant TWO_BRIDGES_MAGIC_VALUE = bytes32(uint256(keccak256("TWO_BRIDGES_MAGIC_VALUE")) - 1);
    /// @dev https://eips.ethereum.org/EIPS/eip-1352
    address constant BRIDGEHUB_MIN_SECOND_BRIDGE_ADDRESS = address(uint160(type(uint16).max));
    /// @dev the maximum number of supported chains, this is an arbitrary limit.
    /// @dev Note, that in case of a malicious Bridgehub admin, the total number of chains
    /// can be up to 2 times higher. This may be possible, in case the old ChainTypeManager
    /// had `100` chains and these were migrated to the Bridgehub only after `MAX_NUMBER_OF_ZK_CHAINS`
    /// were added to the bridgehub via creation of new chains.
    uint256 constant MAX_NUMBER_OF_ZK_CHAINS = 100;
    /// @dev Used as the `msg.sender` for transactions that relayed via a settlement layer.
    address constant SETTLEMENT_LAYER_RELAY_SENDER = address(uint160(0x1111111111111111111111111111111111111111));
    /// @dev The metadata version that is supported by the ZK Chains to prove that an L2->L1 log was included in a batch.
    uint256 constant SUPPORTED_PROOF_METADATA_VERSION = 1;
    /// @dev The virtual address of the L1 settlement layer.
    address constant L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS = address(
        uint160(uint256(keccak256("L1_SETTLEMENT_LAYER_VIRTUAL_ADDRESS")) - 1)
    );
    struct PriorityTreeCommitment {
        uint256 nextLeafIndex;
        uint256 startIndex;
        uint256 unprocessedIndex;
        bytes32[] sides;
    }
    // Info that allows to restore a chain.
    struct ZKChainCommitment {
        /// @notice Total number of executed batches i.e. batches[totalBatchesExecuted] points at the latest executed batch
        /// (batch 0 is genesis)
        uint256 totalBatchesExecuted;
        /// @notice Total number of proved batches i.e. batches[totalBatchesProved] points at the latest proved batch
        uint256 totalBatchesVerified;
        /// @notice Total number of committed batches i.e. batches[totalBatchesCommitted] points at the latest committed
        /// batch
        uint256 totalBatchesCommitted;
        /// @notice The hash of the L2 system contracts ugpgrade transaction.
        /// @dev It is non zero if the migration happens while the upgrade is not yet finalized.
        bytes32 l2SystemContractsUpgradeTxHash;
        /// @notice The batch when the system contracts upgrade transaction was executed.
        /// @dev It is non-zero if the migration happens while the batch where the upgrade tx was present
        /// has not been finalized (executed) yet.
        uint256 l2SystemContractsUpgradeBatchNumber;
        /// @notice The hashes of the batches that are needed to keep the blockchain working.
        /// @dev The length of the array is equal to the `totalBatchesCommitted - totalBatchesExecuted + 1`, i.e. we need
        /// to store all the unexecuted batches' hashes + 1 latest executed one.
        bytes32[] batchHashes;
        /// @notice Commitment to the priority merkle tree.
        PriorityTreeCommitment priorityTree;
        /// @notice Whether a chain is a permanent rollup.
        bool isPermanentRollup;
    }
    /// @dev Used as the `msg.sender` for system service transactions.
    address constant SERVICE_TRANSACTION_SENDER = address(uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF));
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @dev the offset for the system contracts
    uint160 constant SYSTEM_CONTRACTS_OFFSET = 0x8000; // 2^15
    /// @dev The offset from which the built-in, but user space contracts are located.
    uint160 constant USER_CONTRACTS_OFFSET = 0x10000; // 2^16
    /// @dev The formal address of the initial program of the system: the bootloader
    address constant L2_BOOTLOADER_ADDRESS = address(SYSTEM_CONTRACTS_OFFSET + 0x01);
    /// @dev The address of the known code storage system contract
    address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x04);
    /// @dev The address of the L2 deployer system contract.
    address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x06);
    /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed
    /// bytecode.
    /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address.
    /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer
    /// system contract
    /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the
    /// `diamond-initializers` contracts.
    address constant L2_FORCE_DEPLOYER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x07);
    /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
    IL2ToL1Messenger constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = IL2ToL1Messenger(
        address(SYSTEM_CONTRACTS_OFFSET + 0x08)
    );
    /// @dev The address of the eth token system contract
    address constant L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0a);
    /// @dev The address of the context system contract
    address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0b);
    /// @dev The address of the pubdata chunk publisher contract
    address constant L2_PUBDATA_CHUNK_PUBLISHER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x11);
    /// @dev The address used to execute complex upgragedes, also used for the genesis upgrade
    address constant L2_COMPLEX_UPGRADER_ADDR = address(SYSTEM_CONTRACTS_OFFSET + 0x0f);
    /// @dev the address of the msg value system contract
    address constant MSG_VALUE_SYSTEM_CONTRACT = address(SYSTEM_CONTRACTS_OFFSET + 0x09);
    /// @dev The address used to execute the genesis upgrade
    address constant L2_GENESIS_UPGRADE_ADDR = address(USER_CONTRACTS_OFFSET + 0x01);
    /// @dev The address of the L2 bridge hub system contract, used to start L1->L2 transactions
    address constant L2_BRIDGEHUB_ADDR = address(USER_CONTRACTS_OFFSET + 0x02);
    /// @dev the address of the l2 asset router.
    address constant L2_ASSET_ROUTER_ADDR = address(USER_CONTRACTS_OFFSET + 0x03);
    /// @dev An l2 system contract address, used in the assetId calculation for native assets.
    /// This is needed for automatic bridging, i.e. without deploying the AssetHandler contract,
    /// if the assetId can be calculated with this address then it is in fact an NTV asset
    address constant L2_NATIVE_TOKEN_VAULT_ADDR = address(USER_CONTRACTS_OFFSET + 0x04);
    /// @dev the address of the l2 asset router.
    address constant L2_MESSAGE_ROOT_ADDR = address(USER_CONTRACTS_OFFSET + 0x05);
    /**
     * @author Matter Labs
     * @custom:security-contact [email protected]
     * @notice Smart contract for sending arbitrary length messages to L1
     * @dev by default ZkSync can send fixed-length messages on L1.
     * A fixed length message has 4 parameters `senderAddress`, `isService`, `key`, `value`,
     * the first one is taken from the context, the other three are chosen by the sender.
     * @dev To send a variable-length message we use this trick:
     * - This system contract accepts an arbitrary length message and sends a fixed length message with
     * parameters `senderAddress == this`, `isService == true`, `key == msg.sender`, `value == keccak256(message)`.
     * - The contract on L1 accepts all sent messages and if the message came from this system contract
     * it requires that the preimage of `value` be provided.
     */
    interface IL2ToL1Messenger {
        /// @notice Sends an arbitrary length message to L1.
        /// @param _message The variable length message to be sent to L1.
        /// @return Returns the keccak256 hashed value of the message.
        function sendToL1(bytes calldata _message) external returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol";
    import {INativeTokenVault} from "../ntv/INativeTokenVault.sol";
    import {IAssetRouterBase} from "./IAssetRouterBase.sol";
    import {L2TransactionRequestTwoBridgesInner} from "../../bridgehub/IBridgehub.sol";
    import {IL1SharedBridgeLegacy} from "../interfaces/IL1SharedBridgeLegacy.sol";
    import {IL1ERC20Bridge} from "../interfaces/IL1ERC20Bridge.sol";
    /// @title L1 Bridge contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IL1AssetRouter is IAssetRouterBase, IL1SharedBridgeLegacy {
        event BridgehubMintData(bytes bridgeMintData);
        event BridgehubDepositFinalized(
            uint256 indexed chainId,
            bytes32 indexed txDataHash,
            bytes32 indexed l2DepositTxHash
        );
        event ClaimedFailedDepositAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData);
        event AssetDeploymentTrackerSet(
            bytes32 indexed assetId,
            address indexed assetDeploymentTracker,
            bytes32 indexed additionalData
        );
        event LegacyDepositInitiated(
            uint256 indexed chainId,
            bytes32 indexed l2DepositTxHash,
            address indexed from,
            address to,
            address l1Token,
            uint256 amount
        );
        /// @notice Initiates a deposit by locking funds on the contract and sending the request
        /// of processing an L2 transaction where tokens would be minted.
        /// @dev If the token is bridged for the first time, the L2 token contract will be deployed. Note however, that the
        /// newly-deployed token does not support any custom logic, i.e. rebase tokens' functionality is not supported.
        /// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
        /// @param _l2Receiver The account address that should receive funds on L2.
        /// @param _l1Token The L1 token address which is deposited.
        /// @param _amount The total amount of tokens to be bridged.
        /// @param _l2TxGasLimit The L2 gas limit to be used in the corresponding L2 transaction.
        /// @param _l2TxGasPerPubdataByte The gasPerPubdataByteLimit to be used in the corresponding L2 transaction.
        /// @param _refundRecipient The address on L2 that will receive the refund for the transaction.
        /// @dev If the L2 deposit finalization transaction fails, the `_refundRecipient` will receive the `_l2Value`.
        /// Please note, the contract may change the refund recipient's address to eliminate sending funds to addresses
        /// out of control.
        /// - If `_refundRecipient` is a contract on L1, the refund will be sent to the aliased `_refundRecipient`.
        /// - If `_refundRecipient` is set to `address(0)` and the sender has NO deployed bytecode on L1, the refund will
        /// be sent to the `msg.sender` address.
        /// - If `_refundRecipient` is set to `address(0)` and the sender has deployed bytecode on L1, the refund will be
        /// sent to the aliased `msg.sender` address.
        /// @dev The address aliasing of L1 contracts as refund recipient on L2 is necessary to guarantee that the funds
        /// are controllable through the Mailbox, since the Mailbox applies address aliasing to the from address for the
        /// L2 tx if the L1 msg.sender is a contract. Without address aliasing for L1 contracts as refund recipients they
        /// would not be able to make proper L2 tx requests through the Mailbox to use or withdraw the funds from L2, and
        /// the funds would be lost.
        /// @return txHash The L2 transaction hash of deposit finalization.
        function depositLegacyErc20Bridge(
            address _originalCaller,
            address _l2Receiver,
            address _l1Token,
            uint256 _amount,
            uint256 _l2TxGasLimit,
            uint256 _l2TxGasPerPubdataByte,
            address _refundRecipient
        ) external payable returns (bytes32 txHash);
        function L1_NULLIFIER() external view returns (IL1Nullifier);
        function L1_WETH_TOKEN() external view returns (address);
        function nativeTokenVault() external view returns (INativeTokenVault);
        function setAssetDeploymentTracker(bytes32 _assetRegistrationData, address _assetDeploymentTracker) external;
        function setNativeTokenVault(INativeTokenVault _nativeTokenVault) external;
        function setL1Erc20Bridge(IL1ERC20Bridge _legacyBridge) external;
        /// @notice Withdraw funds from the initiated deposit, that failed when finalizing on L2.
        /// @param _chainId The ZK chain id to which the deposit was initiated.
        /// @param _depositSender The address of the entity that initiated the deposit.
        /// @param _assetId The unique identifier of the deposited L1 token.
        /// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
        /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
        function bridgeRecoverFailedTransfer(
            uint256 _chainId,
            address _depositSender,
            bytes32 _assetId,
            bytes calldata _assetData
        ) external;
        /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
        /// @param _chainId The ZK chain id to which deposit was initiated.
        /// @param _depositSender The address of the entity that initiated the deposit.
        /// @param _assetId The unique identifier of the deposited L1 token.
        /// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
        /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization.
        /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed.
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
        /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
        /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization.
        /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
        function bridgeRecoverFailedTransfer(
            uint256 _chainId,
            address _depositSender,
            bytes32 _assetId,
            bytes memory _assetData,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof
        ) external;
        /// @notice Transfers funds to Native Token Vault, if the asset is registered with it. Does nothing for ETH or non-registered tokens.
        /// @dev assetId is not the padded address, but the correct encoded id (NTV stores respective format for IDs)
        /// @param _amount The asset amount to be transferred to native token vault.
        /// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
        function transferFundsToNTV(bytes32 _assetId, uint256 _amount, address _originalCaller) external returns (bool);
        /// @notice Finalize the withdrawal and release funds
        /// @param _chainId The chain ID of the transaction to check
        /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
        /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent
        /// @param _message The L2 withdraw data, stored in an L2 -> L1 message
        /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
        function finalizeWithdrawal(
            uint256 _chainId,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes calldata _message,
            bytes32[] calldata _merkleProof
        ) external;
        /// @notice Initiates a transfer transaction within Bridgehub, used by `requestL2TransactionTwoBridges`.
        /// @param _chainId The chain ID of the ZK chain to which deposit.
        /// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
        /// @param _value The `msg.value` on the target chain tx.
        /// @param _data The calldata for the second bridge deposit.
        /// @return request The data used by the bridgehub to create L2 transaction request to specific ZK chain.
        /// @dev Data has the following abi encoding for legacy deposits:
        /// address _l1Token,
        /// uint256 _amount,
        /// address _l2Receiver
        /// for new deposits:
        /// bytes32 _assetId,
        /// bytes _transferData
        function bridgehubDeposit(
            uint256 _chainId,
            address _originalCaller,
            uint256 _value,
            bytes calldata _data
        ) external payable returns (L2TransactionRequestTwoBridgesInner memory request);
        /// @notice Generates a calldata for calling the deposit finalization on the L2 native token contract.
        // / @param _chainId The chain ID of the ZK chain to which deposit.
        /// @param _sender The address of the deposit initiator.
        /// @param _assetId The deposited asset ID.
        /// @param _assetData The encoded data, which is used by the asset handler to determine L2 recipient and amount. Might include extra information.
        /// @return Returns calldata used on ZK chain.
        function getDepositCalldata(
            address _sender,
            bytes32 _assetId,
            bytes memory _assetData
        ) external view returns (bytes memory);
        /// @notice Allows bridgehub to acquire mintValue for L1->L2 transactions.
        /// @dev If the corresponding L2 transaction fails, refunds are issued to a refund recipient on L2.
        /// @param _chainId The chain ID of the ZK chain to which deposit.
        /// @param _assetId The deposited asset ID.
        /// @param _originalCaller The `msg.sender` address from the external call that initiated current one.
        /// @param _amount The total amount of tokens to be bridged.
        function bridgehubDepositBaseToken(
            uint256 _chainId,
            bytes32 _assetId,
            address _originalCaller,
            uint256 _amount
        ) external payable;
        /// @notice Routes the confirmation to nullifier for backward compatibility.
        /// @notice Confirms the acceptance of a transaction by the Mailbox, as part of the L2 transaction process within Bridgehub.
        /// This function is utilized by `requestL2TransactionTwoBridges` to validate the execution of a transaction.
        /// @param _chainId The chain ID of the ZK chain to which confirm the deposit.
        /// @param _txDataHash The keccak256 hash of 0x01 || abi.encode(bytes32, bytes) to identify deposits.
        /// @param _txHash The hash of the L1->L2 transaction to confirm the deposit.
        function bridgehubConfirmL2Transaction(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external;
        function isWithdrawalFinalized(
            uint256 _chainId,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex
        ) external view returns (bool);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    // 0x5ecf2d7a
    error AccessToFallbackDenied(address target, address invoker);
    // 0x3995f750
    error AccessToFunctionDenied(address target, bytes4 selector, address invoker);
    // 0x6c167909
    error OnlySelfAllowed();
    // 0x52e22c98
    error RestrictionWasNotPresent(address restriction);
    // 0xf126e113
    error RestrictionWasAlreadyPresent(address restriction);
    // 0x3331e9c0
    error CallNotAllowed(bytes call);
    // 0xf6fd7071
    error RemovingPermanentRestriction();
    // 0xfcb9b2e1
    error UnallowedImplementation(bytes32 implementationHash);
    // 0x0dfb42bf
    error AddressAlreadySet(address addr);
    // 0x86bb51b8
    error AddressHasNoCode(address);
    // 0x1f73225f
    error AddressMismatch(address expected, address supplied);
    // 0x5e85ae73
    error AmountMustBeGreaterThanZero();
    // 0xfde974f4
    error AssetHandlerDoesNotExist(bytes32 assetId);
    // 0x1294e9e1
    error AssetIdMismatch(bytes32 expected, bytes32 supplied);
    // 0xfe919e28
    error AssetIdAlreadyRegistered();
    // 0x0bfcef28
    error AlreadyWhitelisted(address);
    // 0x04a0b7e9
    error AssetIdNotSupported(bytes32 assetId);
    // 0x6ef9a972
    error BaseTokenGasPriceDenominatorNotSet();
    // 0x55ad3fd3
    error BatchHashMismatch(bytes32 expected, bytes32 actual);
    // 0x2078a6a0
    error BatchNotExecuted(uint256 batchNumber);
    // 0xbd4455ff
    error BatchNumberMismatch(uint256 expectedBatchNumber, uint256 providedBatchNumber);
    // 0x6cf12312
    error BridgeHubAlreadyRegistered();
    // 0xdb538614
    error BridgeMintNotImplemented();
    // 0xe85392f9
    error CanOnlyProcessOneBatch();
    // 0x00c6ead2
    error CantExecuteUnprovenBatches();
    // 0xe18cb383
    error CantRevertExecutedBatch();
    // 0x24591d89
    error ChainIdAlreadyExists();
    // 0x717a1656
    error ChainIdCantBeCurrentChain();
    // 0xa179f8c9
    error ChainIdMismatch();
    // 0x23f3c357
    error ChainIdNotRegistered(uint256 chainId);
    // 0x8f620a06
    error ChainIdTooBig();
    // 0xf7a01e4d
    error DelegateCallFailed(bytes returnData);
    // 0x0a8ed92c
    error DenominatorIsZero();
    // 0xb4f54111
    error DeployFailed();
    // 0x138ee1a3
    error DeployingBridgedTokenForNativeToken();
    // 0xc7c9660f
    error DepositDoesNotExist();
    // 0xad2fa98e
    error DepositExists();
    // 0x0e7ee319
    error DiamondAlreadyFrozen();
    // 0xa7151b9a
    error DiamondNotFrozen();
    // 0x7138356f
    error EmptyAddress();
    // 0x2d4d012f
    error EmptyAssetId();
    // 0x1c25715b
    error EmptyBytes32();
    // 0x95b66fe9
    error EmptyDeposit();
    // 0x627e0872
    error ETHDepositNotSupported();
    // 0xac4a3f98
    error FacetExists(bytes4 selector, address);
    // 0xc91cf3b1
    error GasPerPubdataMismatch();
    // 0x6d4a7df8
    error GenesisBatchCommitmentZero();
    // 0x7940c83f
    error GenesisBatchHashZero();
    // 0xb4fc6835
    error GenesisIndexStorageZero();
    // 0x3a1a8589
    error GenesisUpgradeZero();
    // 0xd356e6ba
    error HashedLogIsDefault();
    // 0x0b08d5be
    error HashMismatch(bytes32 expected, bytes32 actual);
    // 0x601b6882
    error ZKChainLimitReached();
    // 0xdd381a4c
    error IncorrectBridgeHubAddress(address bridgehub);
    // 0x826fb11e
    error InsufficientChainBalance();
    // 0xcbd9d2e0
    error InvalidCaller(address);
    // 0x4fbe5dba
    error InvalidDelay();
    // 0xc1780bd6
    error InvalidLogSender(address sender, uint256 logKey);
    // 0xd8e9405c
    error InvalidNumberOfBlobs(uint256 expected, uint256 numCommitments, uint256 numHashes);
    // 0x09bde339
    error InvalidProof();
    // 0x5428eae7
    error InvalidProtocolVersion();
    // 0x6f1cf752
    error InvalidPubdataPricingMode();
    // 0x12ba286f
    error InvalidSelector(bytes4 func);
    // 0x0214acb6
    error InvalidUpgradeTxn(UpgradeTxVerifyParam);
    // 0xfb5c22e6
    error L2TimestampTooBig();
    // 0x97e1359e
    error L2WithdrawalMessageWrongLength(uint256 messageLen);
    // 0xe37d2c02
    error LengthIsNotDivisibleBy32(uint256 length);
    // 0x1b6825bb
    error LogAlreadyProcessed(uint8);
    // 0xcea34703
    error MalformedBytecode(BytecodeError);
    // 0x9bb54c35
    error MerkleIndexOutOfBounds();
    // 0x8e23ac1a
    error MerklePathEmpty();
    // 0x1c500385
    error MerklePathOutOfBounds();
    // 0x3312a450
    error MigrationPaused();
    // 0xfa44b527
    error MissingSystemLogs(uint256 expected, uint256 actual);
    // 0x4a094431
    error MsgValueMismatch(uint256 expectedMsgValue, uint256 providedMsgValue);
    // 0xb385a3da
    error MsgValueTooLow(uint256 required, uint256 provided);
    // 0x79cc2d22
    error NoCallsProvided();
    // 0xa6fef710
    error NoFunctionsForDiamondCut();
    // 0xcab098d8
    error NoFundsTransferred();
    // 0xc21b1ab7
    error NonEmptyCalldata();
    // 0x536ec84b
    error NonEmptyMsgValue();
    // 0xd018e08e
    error NonIncreasingTimestamp();
    // 0x0105f9c0
    error NonSequentialBatch();
    // 0x0ac76f01
    error NonSequentialVersion();
    // 0xdd7e3621
    error NotInitializedReentrancyGuard();
    // 0xdf17e316
    error NotWhitelisted(address);
    // 0xf3ed9dfa
    error OnlyEraSupported();
    // 0x1a21feed
    error OperationExists();
    // 0xeda2fbb1
    error OperationMustBePending();
    // 0xe1c1ff37
    error OperationMustBeReady();
    // 0xb926450e
    error OriginChainIdNotFound();
    // 0x9b48e060
    error PreviousOperationNotExecuted();
    // 0xd5a99014
    error PriorityOperationsRollingHashMismatch();
    // 0x1a4d284a
    error PriorityTxPubdataExceedsMaxPubDataPerBatch();
    // 0xa461f651
    error ProtocolIdMismatch(uint256 expectedProtocolVersion, uint256 providedProtocolId);
    // 0x64f94ec2
    error ProtocolIdNotGreater();
    // 0x959f26fb
    error PubdataGreaterThanLimit(uint256 limit, uint256 length);
    // 0x63c36549
    error QueueIsEmpty();
    // 0xab143c06
    error Reentrancy();
    // 0x667d17de
    error RemoveFunctionFacetAddressNotZero(address facet);
    // 0xa2d4b16c
    error RemoveFunctionFacetAddressZero();
    // 0x3580370c
    error ReplaceFunctionFacetAddressZero();
    // 0x9a67c1cb
    error RevertedBatchNotAfterNewLastBatch();
    // 0xd3b6535b
    error SelectorsMustAllHaveSameFreezability();
    // 0xd7a6b5e6
    error SharedBridgeValueNotSet(SharedBridgeKey);
    // 0x856d5b77
    error SharedBridgeNotSet();
    // 0xdf3a8fdd
    error SlotOccupied();
    // 0xec273439
    error CTMAlreadyRegistered();
    // 0xc630ef3c
    error CTMNotRegistered();
    // 0xae43b424
    error SystemLogsSizeTooBig();
    // 0x08753982
    error TimeNotReached(uint256 expectedTimestamp, uint256 actualTimestamp);
    // 0x2d50c33b
    error TimestampError();
    // 0x06439c6b
    error TokenNotSupported(address token);
    // 0x23830e28
    error TokensWithFeesNotSupported();
    // 0x76da24b9
    error TooManyFactoryDeps();
    // 0xf0b4e88f
    error TooMuchGas();
    // 0x00c5a6a9
    error TransactionNotAllowed();
    // 0x4c991078
    error TxHashMismatch();
    // 0x2e311df8
    error TxnBodyGasLimitNotEnoughGas();
    // 0x8e4a23d6
    error Unauthorized(address caller);
    // 0xe52478c7
    error UndefinedDiamondCutAction();
    // 0x6aa39880
    error UnexpectedSystemLog(uint256 logKey);
    // 0xf093c2e5
    error UpgradeBatchNumberIsNotZero();
    // 0x084a1449
    error UnsupportedEncodingVersion();
    // 0x47b3b145
    error ValidateTxnNotEnoughGas();
    // 0x626ade30
    error ValueMismatch(uint256 expected, uint256 actual);
    // 0xe1022469
    error VerifiedBatchesExceedsCommittedBatches();
    // 0xae899454
    error WithdrawalAlreadyFinalized();
    // 0x750b219c
    error WithdrawFailed();
    // 0x15e8e429
    error WrongMagicValue(uint256 expectedMagicValue, uint256 providedMagicValue);
    // 0xd92e233d
    error ZeroAddress();
    // 0xc84885d4
    error ZeroChainId();
    // 0x99d8fec9
    error EmptyData();
    // 0xf3dd1b9c
    error UnsupportedCommitBatchEncoding(uint8 version);
    // 0xf338f830
    error UnsupportedProofBatchEncoding(uint8 version);
    // 0x14d2ed8a
    error UnsupportedExecuteBatchEncoding(uint8 version);
    // 0xd7d93e1f
    error IncorrectBatchBounds(
        uint256 processFromExpected,
        uint256 processToExpected,
        uint256 processFromProvided,
        uint256 processToProvided
    );
    // 0x64107968
    error AssetHandlerNotRegistered(bytes32 assetId);
    // 0x64846fe4
    error NotARestriction(address addr);
    // 0xfa5cd00f
    error NotAllowed(address addr);
    // 0xccdd18d2
    error BytecodeAlreadyPublished(bytes32 bytecodeHash);
    // 0x25d8333c
    error CallerNotTimerAdmin();
    // 0x907f8e51
    error DeadlineNotYetPassed();
    // 0x6eef58d1
    error NewDeadlineNotGreaterThanCurrent();
    // 0x8b7e144a
    error NewDeadlineExceedsMaxDeadline();
    // 0x2a5989a0
    error AlreadyPermanentRollup();
    // 0x92daded2
    error InvalidDAForPermanentRollup();
    // 0x7a4902ad
    error TimerAlreadyStarted();
    // 0x09aa9830
    error MerklePathLengthMismatch(uint256 pathLength, uint256 expectedLength);
    // 0xc33e6128
    error MerkleNothingToProve();
    // 0xafbb7a4e
    error MerkleIndexOrHeightMismatch();
    // 0x1b582fcf
    error MerkleWrongIndex(uint256 index, uint256 maxNodeNumber);
    // 0x485cfcaa
    error MerkleWrongLength(uint256 newLeavesLength, uint256 leafNumber);
    // 0xce63ce17
    error NoCTMForAssetId(bytes32 assetId);
    // 0x02181a13
    error SettlementLayersMustSettleOnL1();
    // 0x1850b46b
    error TokenNotLegacy();
    // 0x1929b7de
    error IncorrectTokenAddressFromNTV(bytes32 assetId, address tokenAddress);
    // 0x48c5fa28
    error InvalidProofLengthForFinalNode();
    // 0xfade089a
    error LegacyEncodingUsedForNonL1Token();
    // 0xa51fa558
    error TokenIsLegacy();
    // 0x29963361
    error LegacyBridgeUsesNonNativeToken();
    // 0x11832de8
    error AssetRouterAllowanceNotZero();
    // 0xaa5f6180
    error BurningNativeWETHNotSupported();
    // 0xb20b58ce
    error NoLegacySharedBridge();
    // 0x8e3ce3cb
    error TooHighDeploymentNonce();
    // 0x78d2ed02
    error ChainAlreadyLive();
    // 0x4e98b356
    error MigrationsNotPaused();
    // 0xf20c5c2a
    error WrappedBaseTokenAlreadyRegistered();
    // 0xde4c0b96
    error InvalidNTVBurnData();
    // 0xbe7193d4
    error InvalidSystemLogsLength();
    // 0x8efef97a
    error LegacyBridgeNotSet();
    // 0x767eed08
    error LegacyMethodForNonL1Token();
    // 0xc352bb73
    error UnknownVerifierType();
    // 0x456f8f7a
    error EmptyProofLength();
    enum SharedBridgeKey {
        PostUpgradeFirstBatch,
        LegacyBridgeFirstBatch,
        LegacyBridgeLastDepositBatch,
        LegacyBridgeLastDepositTxn
    }
    enum BytecodeError {
        Version,
        NumberOfWords,
        Length,
        WordsMustBeOdd
    }
    enum UpgradeTxVerifyParam {
        From,
        To,
        Paymaster,
        Value,
        MaxFeePerGas,
        MaxPriorityFeePerGas,
        Reserved0,
        Reserved1,
        Reserved2,
        Reserved3,
        Signature,
        PaymasterInput,
        ReservedDynamic
    }
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.21;
    // 0x2e89f517
    error L1DAValidatorAddressIsZero();
    // 0x944bc075
    error L2DAValidatorAddressIsZero();
    // 0xca1c3cbc
    error AlreadyMigrated();
    // 0xf05c64c6
    error NotChainAdmin(address prevMsgSender, address admin);
    // 0xc59d372c
    error ProtocolVersionNotUpToDate(uint256 currentProtocolVersion, uint256 protocolVersion);
    // 0xedae13f3
    error ExecutedIsNotConsistentWithVerified(uint256 batchesExecuted, uint256 batchesVerified);
    // 0x712d02d2
    error VerifiedIsNotConsistentWithCommitted(uint256 batchesVerified, uint256 batchesCommitted);
    // 0xfb1a3b59
    error InvalidNumberOfBatchHashes(uint256 batchHashesLength, uint256 expected);
    // 0xa840274f
    error PriorityQueueNotReady();
    // 0x79274f04
    error UnsupportedProofMetadataVersion(uint256 metadataVersion);
    // 0xa969e486
    error LocalRootIsZero();
    // 0xbdaf7d42
    error LocalRootMustBeZero();
    // 0xd0266e26
    error NotSettlementLayer();
    // 0x32ddf9a2
    error NotHyperchain();
    // 0x2237c426
    error MismatchL2DAValidator();
    // 0x2c01a4af
    error MismatchNumberOfLayer1Txs(uint256 numberOfLayer1Txs, uint256 expectedLength);
    // 0xfbd630b8
    error InvalidBatchesDataLength(uint256 batchesDataLength, uint256 priorityOpsDataLength);
    // 0x55008233
    error PriorityOpsDataLeftPathLengthIsNotZero();
    // 0x8be936a9
    error PriorityOpsDataRightPathLengthIsNotZero();
    // 0x99d44739
    error PriorityOpsDataItemHashesLengthIsNotZero();
    // 0x885ae069
    error OperatorDAInputTooSmall(uint256 operatorDAInputLength, uint256 minAllowedLength);
    // 0xbeb96791
    error InvalidNumberOfBlobs(uint256 blobsProvided, uint256 maxBlobsSupported);
    // 0xd2531c15
    error InvalidL2DAOutputHash(bytes32 l2DAValidatorOutputHash);
    // 0x04e05fd1
    error OnlyOneBlobWithCalldataAllowed();
    // 0x2dc9747d
    error PubdataInputTooSmall(uint256 pubdataInputLength, uint256 totalBlobsCommitmentSize);
    // 0x9044dff9
    error PubdataLengthTooBig(uint256 pubdataLength, uint256 totalBlobSizeBytes);
    // 0x5513177c
    error InvalidPubdataHash(bytes32 fullPubdataHash, bytes32 providedPubdataHash);
    // 0x5717f940
    error InvalidPubdataSource(uint8 pubdataSource);
    // 0x125d99b0
    error BlobHashBlobCommitmentMismatchValue();
    // 0x7fbff2dd
    error L1DAValidatorInvalidSender(address msgSender);
    // 0xc06789fa
    error InvalidCommitment();
    // 0xc866ff2c
    error InitialForceDeploymentMismatch(bytes32 forceDeploymentHash, bytes32 initialForceDeploymentHash);
    // 0xb325f767
    error AdminZero();
    // 0x681150be
    error OutdatedProtocolVersion(uint256 protocolVersion, uint256 currentProtocolVersion);
    // 0x87470e36
    error NotL1(uint256 blockChainId);
    // 0x90f67ecf
    error InvalidStartIndex(uint256 treeStartIndex, uint256 commitmentStartIndex);
    // 0x0f67bc0a
    error InvalidUnprocessedIndex(uint256 treeUnprocessedIndex, uint256 commitmentUnprocessedIndex);
    // 0x30043900
    error InvalidNextLeafIndex(uint256 treeNextLeafIndex, uint256 commitmentNextLeafIndex);
    // 0xf9ba09d6
    error NotAllBatchesExecuted();
    // 0x9b53b101
    error NotHistoricalRoot();
    // 0xc02d3ee3
    error ContractNotDeployed();
    // 0xd7b2559b
    error NotMigrated();
    // 0x52595598
    error ValL1DAWrongInputLength(uint256 inputLength, uint256 expectedLength);
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @title The interface of the ZKsync contract, responsible for the main ZKsync logic.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IZKChainBase {
        /// @return Returns facet name.
        function getName() external view returns (string memory);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {SafeCast} from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol";
    import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol";
    import {NoFunctionsForDiamondCut, UndefinedDiamondCutAction, AddressHasNoCode, FacetExists, RemoveFunctionFacetAddressZero, SelectorsMustAllHaveSameFreezability, NonEmptyCalldata, ReplaceFunctionFacetAddressZero, RemoveFunctionFacetAddressNotZero, DelegateCallFailed} from "../../common/L1ContractErrors.sol";
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice The helper library for managing the EIP-2535 diamond proxy.
    library Diamond {
        using UncheckedMath for uint256;
        using SafeCast for uint256;
        /// @dev Magic value that should be returned by diamond cut initialize contracts.
        /// @dev Used to distinguish calls to contracts that were supposed to be used as diamond initializer from other contracts.
        bytes32 internal constant DIAMOND_INIT_SUCCESS_RETURN_VALUE =
            0x33774e659306e47509050e97cb651e731180a42d458212294d30751925c551a2; // keccak256("diamond.zksync.init") - 1
        /// @dev Storage position of `DiamondStorage` structure.
        bytes32 private constant DIAMOND_STORAGE_POSITION =
            0xc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131b; // keccak256("diamond.standard.diamond.storage") - 1;
        event DiamondCut(FacetCut[] facetCuts, address initAddress, bytes initCalldata);
        /// @dev Utility struct that contains associated facet & meta information of selector
        /// @param facetAddress address of the facet which is connected with selector
        /// @param selectorPosition index in `FacetToSelectors.selectors` array, where is selector stored
        /// @param isFreezable denotes whether the selector can be frozen.
        struct SelectorToFacet {
            address facetAddress;
            uint16 selectorPosition;
            bool isFreezable;
        }
        /// @dev Utility struct that contains associated selectors & meta information of facet
        /// @param selectors list of all selectors that belong to the facet
        /// @param facetPosition index in `DiamondStorage.facets` array, where is facet stored
        struct FacetToSelectors {
            bytes4[] selectors;
            uint16 facetPosition;
        }
        /// @notice The structure that holds all diamond proxy associated parameters
        /// @dev According to the EIP-2535 should be stored on a special storage key - `DIAMOND_STORAGE_POSITION`
        /// @param selectorToFacet A mapping from the selector to the facet address and its meta information
        /// @param facetToSelectors A mapping from facet address to its selectors with meta information
        /// @param facets The array of all unique facet addresses that belong to the diamond proxy
        /// @param isFrozen Denotes whether the diamond proxy is frozen and all freezable facets are not accessible
        struct DiamondStorage {
            mapping(bytes4 selector => SelectorToFacet selectorInfo) selectorToFacet;
            mapping(address facetAddress => FacetToSelectors facetInfo) facetToSelectors;
            address[] facets;
            bool isFrozen;
        }
        /// @dev Parameters for diamond changes that touch one of the facets
        /// @param facet The address of facet that's affected by the cut
        /// @param action The action that is made on the facet
        /// @param isFreezable Denotes whether the facet & all their selectors can be frozen
        /// @param selectors An array of unique selectors that belongs to the facet address
        // solhint-disable-next-line gas-struct-packing
        struct FacetCut {
            address facet;
            Action action;
            bool isFreezable;
            bytes4[] selectors;
        }
        /// @dev Structure of the diamond proxy changes
        /// @param facetCuts The set of changes (adding/removing/replacement) of implementation contracts
        /// @param initAddress The address that's delegate called after setting up new facet changes
        /// @param initCalldata Calldata for the delegate call to `initAddress`
        struct DiamondCutData {
            FacetCut[] facetCuts;
            address initAddress;
            bytes initCalldata;
        }
        /// @dev Type of change over diamond: add/replace/remove facets
        enum Action {
            Add,
            Replace,
            Remove
        }
        /// @return diamondStorage The pointer to the storage where all specific diamond proxy parameters stored
        function getDiamondStorage() internal pure returns (DiamondStorage storage diamondStorage) {
            bytes32 position = DIAMOND_STORAGE_POSITION;
            assembly {
                diamondStorage.slot := position
            }
        }
        /// @dev Add/replace/remove any number of selectors and optionally execute a function with delegatecall
        /// @param _diamondCut Diamond's facet changes and the parameters to optional initialization delegatecall
        function diamondCut(DiamondCutData memory _diamondCut) internal {
            FacetCut[] memory facetCuts = _diamondCut.facetCuts;
            address initAddress = _diamondCut.initAddress;
            bytes memory initCalldata = _diamondCut.initCalldata;
            uint256 facetCutsLength = facetCuts.length;
            for (uint256 i = 0; i < facetCutsLength; i = i.uncheckedInc()) {
                Action action = facetCuts[i].action;
                address facet = facetCuts[i].facet;
                bool isFacetFreezable = facetCuts[i].isFreezable;
                bytes4[] memory selectors = facetCuts[i].selectors;
                if (selectors.length == 0) {
                    revert NoFunctionsForDiamondCut();
                }
                if (action == Action.Add) {
                    _addFunctions(facet, selectors, isFacetFreezable);
                } else if (action == Action.Replace) {
                    _replaceFunctions(facet, selectors, isFacetFreezable);
                } else if (action == Action.Remove) {
                    _removeFunctions(facet, selectors);
                } else {
                    revert UndefinedDiamondCutAction();
                }
            }
            _initializeDiamondCut(initAddress, initCalldata);
            emit DiamondCut(facetCuts, initAddress, initCalldata);
        }
        /// @dev Add new functions to the diamond proxy
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _addFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Facet with no code cannot be added.
            // This check also verifies that the facet does not have zero address, since it is the
            // address with which 0x00000000 selector is associated.
            if (_facet.code.length == 0) {
                revert AddressHasNoCode(_facet);
            }
            // Add facet to the list of facets if the facet address is new one
            _saveFacetIfNew(_facet);
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                if (oldFacet.facetAddress != address(0)) {
                    revert FacetExists(selector, oldFacet.facetAddress);
                }
                _addOneFunction(_facet, selector, _isFacetFreezable);
            }
        }
        /// @dev Change associated facets to already known function selectors
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _replaceFunctions(address _facet, bytes4[] memory _selectors, bool _isFacetFreezable) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Facet with no code cannot be added.
            // This check also verifies that the facet does not have zero address, since it is the
            // address with which 0x00000000 selector is associated.
            if (_facet.code.length == 0) {
                revert AddressHasNoCode(_facet);
            }
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                // it is impossible to replace the facet with zero address
                if (oldFacet.facetAddress == address(0)) {
                    revert ReplaceFunctionFacetAddressZero();
                }
                _removeOneFunction(oldFacet.facetAddress, selector);
                // Add facet to the list of facets if the facet address is a new one
                _saveFacetIfNew(_facet);
                _addOneFunction(_facet, selector, _isFacetFreezable);
            }
        }
        /// @dev Remove association with function and facet
        /// NOTE: expect but NOT enforce that `_selectors` is NON-EMPTY array
        function _removeFunctions(address _facet, bytes4[] memory _selectors) private {
            DiamondStorage storage ds = getDiamondStorage();
            // facet address must be zero
            if (_facet != address(0)) {
                revert RemoveFunctionFacetAddressNotZero(_facet);
            }
            uint256 selectorsLength = _selectors.length;
            for (uint256 i = 0; i < selectorsLength; i = i.uncheckedInc()) {
                bytes4 selector = _selectors[i];
                SelectorToFacet memory oldFacet = ds.selectorToFacet[selector];
                // Can't delete a non-existent facet
                if (oldFacet.facetAddress == address(0)) {
                    revert RemoveFunctionFacetAddressZero();
                }
                _removeOneFunction(oldFacet.facetAddress, selector);
            }
        }
        /// @dev Add address to the list of known facets if it is not on the list yet
        /// NOTE: should be called ONLY before adding a new selector associated with the address
        function _saveFacetIfNew(address _facet) private {
            DiamondStorage storage ds = getDiamondStorage();
            uint256 selectorsLength = ds.facetToSelectors[_facet].selectors.length;
            // If there are no selectors associated with facet then save facet as new one
            if (selectorsLength == 0) {
                ds.facetToSelectors[_facet].facetPosition = ds.facets.length.toUint16();
                ds.facets.push(_facet);
            }
        }
        /// @dev Add one function to the already known facet
        /// NOTE: It is expected but NOT enforced that:
        /// - `_facet` is NON-ZERO address
        /// - `_facet` is already stored address in `DiamondStorage.facets`
        /// - `_selector` is NOT associated by another facet
        function _addOneFunction(address _facet, bytes4 _selector, bool _isSelectorFreezable) private {
            DiamondStorage storage ds = getDiamondStorage();
            uint16 selectorPosition = (ds.facetToSelectors[_facet].selectors.length).toUint16();
            // if selectorPosition is nonzero, it means it is not a new facet
            // so the freezability of the first selector must be matched to _isSelectorFreezable
            // so all the selectors in a facet will have the same freezability
            if (selectorPosition != 0) {
                bytes4 selector0 = ds.facetToSelectors[_facet].selectors[0];
                if (_isSelectorFreezable != ds.selectorToFacet[selector0].isFreezable) {
                    revert SelectorsMustAllHaveSameFreezability();
                }
            }
            ds.selectorToFacet[_selector] = SelectorToFacet({
                facetAddress: _facet,
                selectorPosition: selectorPosition,
                isFreezable: _isSelectorFreezable
            });
            ds.facetToSelectors[_facet].selectors.push(_selector);
        }
        /// @dev Remove one associated function with facet
        /// NOTE: It is expected but NOT enforced that `_facet` is NON-ZERO address
        function _removeOneFunction(address _facet, bytes4 _selector) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Get index of `FacetToSelectors.selectors` of the selector and last element of array
            uint256 selectorPosition = ds.selectorToFacet[_selector].selectorPosition;
            uint256 lastSelectorPosition = ds.facetToSelectors[_facet].selectors.length - 1;
            // If the selector is not at the end of the array then move the last element to the selector position
            if (selectorPosition != lastSelectorPosition) {
                bytes4 lastSelector = ds.facetToSelectors[_facet].selectors[lastSelectorPosition];
                ds.facetToSelectors[_facet].selectors[selectorPosition] = lastSelector;
                ds.selectorToFacet[lastSelector].selectorPosition = selectorPosition.toUint16();
            }
            // Remove last element from the selectors array
            ds.facetToSelectors[_facet].selectors.pop();
            // Finally, clean up the association with facet
            delete ds.selectorToFacet[_selector];
            // If there are no selectors for facet then remove the facet from the list of known facets
            if (lastSelectorPosition == 0) {
                _removeFacet(_facet);
            }
        }
        /// @dev remove facet from the list of known facets
        /// NOTE: It is expected but NOT enforced that there are no selectors associated with `_facet`
        function _removeFacet(address _facet) private {
            DiamondStorage storage ds = getDiamondStorage();
            // Get index of `DiamondStorage.facets` of the facet and last element of array
            uint256 facetPosition = ds.facetToSelectors[_facet].facetPosition;
            uint256 lastFacetPosition = ds.facets.length - 1;
            // If the facet is not at the end of the array then move the last element to the facet position
            if (facetPosition != lastFacetPosition) {
                address lastFacet = ds.facets[lastFacetPosition];
                ds.facets[facetPosition] = lastFacet;
                ds.facetToSelectors[lastFacet].facetPosition = facetPosition.toUint16();
            }
            // Remove last element from the facets array
            ds.facets.pop();
        }
        /// @dev Delegates call to the initialization address with provided calldata
        /// @dev Used as a final step of diamond cut to execute the logic of the initialization for changed facets
        function _initializeDiamondCut(address _init, bytes memory _calldata) private {
            if (_init == address(0)) {
                // Non-empty calldata for zero address
                if (_calldata.length != 0) {
                    revert NonEmptyCalldata();
                }
            } else {
                // Do not check whether `_init` is a contract since later we check that it returns data.
                (bool success, bytes memory data) = _init.delegatecall(_calldata);
                if (!success) {
                    // If the returndata is too small, we still want to produce some meaningful error
                    if (data.length < 4) {
                        revert DelegateCallFailed(data);
                    }
                    assembly {
                        revert(add(data, 0x20), mload(data))
                    }
                }
                // Check that called contract returns magic value to make sure that contract logic
                // supposed to be used as diamond cut initializer.
                if (data.length != 32) {
                    revert DelegateCallFailed(data);
                }
                if (abi.decode(data, (bytes32)) != DIAMOND_INIT_SUCCESS_RETURN_VALUE) {
                    revert DelegateCallFailed(data);
                }
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    /// @title L1 Asset Handler contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice Used for any asset handler and called by the L1AssetRouter
    interface IL1AssetHandler {
        /// @param _chainId the chainId that the message will be sent to
        /// @param _assetId the assetId of the asset being bridged
        /// @param _depositSender the address of the entity that initiated the deposit.
        /// @param _data the actual data specified for the function
        function bridgeRecoverFailedTransfer(
            uint256 _chainId,
            bytes32 _assetId,
            address _depositSender,
            bytes calldata _data
        ) external payable;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {L2TransactionRequestTwoBridgesInner, IBridgehub} from "./IBridgehub.sol";
    import {IAssetRouterBase} from "../bridge/asset-router/IAssetRouterBase.sol";
    import {IL1AssetDeploymentTracker} from "../bridge/interfaces/IL1AssetDeploymentTracker.sol";
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface ICTMDeploymentTracker is IL1AssetDeploymentTracker {
        function bridgehubDeposit(
            uint256 _chainId,
            address _originalCaller,
            uint256 _l2Value,
            bytes calldata _data
        ) external payable returns (L2TransactionRequestTwoBridgesInner memory request);
        function BRIDGE_HUB() external view returns (IBridgehub);
        function L1_ASSET_ROUTER() external view returns (IAssetRouterBase);
        function registerCTMAssetOnL1(address _ctmAddress) external;
        function calculateAssetId(address _l1CTM) external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IBridgehub} from "./IBridgehub.sol";
    /**
     * @author Matter Labs
     * @notice MessageRoot contract is responsible for storing and aggregating the roots of the batches from different chains into the MessageRoot.
     * @custom:security-contact [email protected]
     */
    interface IMessageRoot {
        function BRIDGE_HUB() external view returns (IBridgehub);
        function addNewChain(uint256 _chainId) external;
        function addChainBatchRoot(uint256 _chainId, uint256 _batchNumber, bytes32 _chainBatchRoot) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    /// @title Asset Handler contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice Used for any asset handler and called by the AssetRouter
    interface IAssetHandler {
        /// @dev Emitted when a token is minted
        event BridgeMint(uint256 indexed chainId, bytes32 indexed assetId, address receiver, uint256 amount);
        /// @dev Emitted when a token is burned
        event BridgeBurn(
            uint256 indexed chainId,
            bytes32 indexed assetId,
            address indexed sender,
            address receiver,
            uint256 amount
        );
        /// @param _chainId the chainId that the message is from
        /// @param _assetId the assetId of the asset being bridged
        /// @param _data the actual data specified for the function
        /// @dev Note, that while payable, this function will only receive base token on L2 chains,
        /// while L1 the provided msg.value is always 0. However, this may change in the future,
        /// so if your AssetHandler implementation relies on it, it is better to explicitly check it.
        function bridgeMint(uint256 _chainId, bytes32 _assetId, bytes calldata _data) external payable;
        /// @notice Burns bridged tokens and returns the calldata for L2 <-> L1 message.
        /// @dev In case of native token vault _data is the tuple of _depositAmount and _l2Receiver.
        /// @param _chainId the chainId that the message will be sent to
        /// @param _msgValue the msg.value of the L2 transaction. For now it is always 0.
        /// @param _assetId the assetId of the asset being bridged
        /// @param _originalCaller the original caller of the
        /// @param _data the actual data specified for the function
        /// @return _bridgeMintData The calldata used by counterpart asset handler to unlock tokens for recipient.
        function bridgeBurn(
            uint256 _chainId,
            uint256 _msgValue,
            bytes32 _assetId,
            address _originalCaller,
            bytes calldata _data
        ) external payable returns (bytes memory _bridgeMintData);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {Merkle} from "./Merkle.sol";
    import {Arrays} from "@openzeppelin/contracts-v4/utils/Arrays.sol";
    /**
     * @dev Library for managing https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures.
     *
     * Each tree is a complete binary tree with the ability to sequentially insert leaves, changing them from a zero to a
     * non-zero value and updating its root. This structure allows inserting commitments (or other entries) that are not
     * stored, but can be proven to be part of the tree at a later time if the root is kept. See {MerkleProof}.
     *
     * A tree is defined by the following parameters:
     *
     * * Depth: The number of levels in the tree, it also defines the maximum number of leaves as 2**depth.
     * * Zero value: The value that represents an empty leaf. Used to avoid regular zero values to be part of the tree.
     * * Hashing function: A cryptographic hash function used to produce internal nodes.
     *
     * This is a fork of OpenZeppelin's [`MerkleTree`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/9af280dc4b45ee5bda96ba47ff829b407eaab67e/contracts/utils/structs/MerkleTree.sol)
     * library, with the changes to support dynamic tree growth (doubling the size when full).
     */
    library DynamicIncrementalMerkle {
        /**
         * @dev A complete `bytes32` Merkle tree.
         *
         * The `sides` and `zero` arrays are set to have a length equal to the depth of the tree during setup.
         *
         * Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
         * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
         * lead to unexpected behavior.
         *
         * NOTE: The `root` and the updates history is not stored within the tree. Consider using a secondary structure to
         * store a list of historical roots from the values returned from {setup} and {push} (e.g. a mapping, {BitMaps} or
         * {Checkpoints}).
         *
         * WARNING: Updating any of the tree's parameters after the first insertion will result in a corrupted tree.
         */
        struct Bytes32PushTree {
            uint256 _nextLeafIndex;
            bytes32[] _sides;
            bytes32[] _zeros;
        }
        /**
         * @dev Initialize a {Bytes32PushTree} using {Hashes-Keccak256} to hash internal nodes.
         * The capacity of the tree (i.e. number of leaves) is set to `2**levels`.
         *
         * IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing
         * empty leaves. It should be a value that is not expected to be part of the tree.
         */
        function setup(Bytes32PushTree storage self, bytes32 zero) internal returns (bytes32 initialRoot) {
            self._nextLeafIndex = 0;
            self._zeros.push(zero);
            self._sides.push(bytes32(0));
            return bytes32(0);
        }
        /**
         * @dev Resets the tree to a blank state.
         * Calling this function on MerkleTree that was already setup and used will reset it to a blank state.
         * @param zero The value that represents an empty leaf.
         * @return initialRoot The initial root of the tree.
         */
        function reset(Bytes32PushTree storage self, bytes32 zero) internal returns (bytes32 initialRoot) {
            self._nextLeafIndex = 0;
            uint256 length = self._zeros.length;
            for (uint256 i = length; 0 < i; --i) {
                self._zeros.pop();
            }
            length = self._sides.length;
            for (uint256 i = length; 0 < i; --i) {
                self._sides.pop();
            }
            self._zeros.push(zero);
            self._sides.push(bytes32(0));
            return bytes32(0);
        }
        /**
         * @dev Insert a new leaf in the tree, and compute the new root. Returns the position of the inserted leaf in the
         * tree, and the resulting root.
         *
         * Hashing the leaf before calling this function is recommended as a protection against
         * second pre-image attacks.
         */
        function push(Bytes32PushTree storage self, bytes32 leaf) internal returns (uint256 index, bytes32 newRoot) {
            // Cache read
            uint256 levels = self._zeros.length - 1;
            // Get leaf index
            // solhint-disable-next-line gas-increment-by-one
            index = self._nextLeafIndex++;
            // Check if tree is full.
            if (index == 1 << levels) {
                bytes32 zero = self._zeros[levels];
                bytes32 newZero = Merkle.efficientHash(zero, zero);
                self._zeros.push(newZero);
                self._sides.push(bytes32(0));
                ++levels;
            }
            // Rebuild branch from leaf to root
            uint256 currentIndex = index;
            bytes32 currentLevelHash = leaf;
            bool updatedSides = false;
            for (uint32 i = 0; i < levels; ++i) {
                // Reaching the parent node, is currentLevelHash the left child?
                bool isLeft = currentIndex % 2 == 0;
                // If so, next time we will come from the right, so we need to save it
                if (isLeft && !updatedSides) {
                    Arrays.unsafeAccess(self._sides, i).value = currentLevelHash;
                    updatedSides = true;
                }
                // Compute the current node hash by using the hash function
                // with either its sibling (side) or the zero value for that level.
                currentLevelHash = Merkle.efficientHash(
                    isLeft ? currentLevelHash : Arrays.unsafeAccess(self._sides, i).value,
                    isLeft ? Arrays.unsafeAccess(self._zeros, i).value : currentLevelHash
                );
                // Update node index
                currentIndex >>= 1;
            }
            Arrays.unsafeAccess(self._sides, levels).value = currentLevelHash;
            return (index, currentLevelHash);
        }
        /**
         * @dev Tree's root.
         */
        function root(Bytes32PushTree storage self) internal view returns (bytes32) {
            return Arrays.unsafeAccess(self._sides, self._sides.length - 1).value;
        }
        /**
         * @dev Tree's height (does not include the root node).
         */
        function height(Bytes32PushTree storage self) internal view returns (uint256) {
            return self._sides.length - 1;
        }
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    /// @notice Part of the configuration parameters of ZKP circuits
    struct VerifierParams {
        bytes32 recursionNodeLevelVkHash;
        bytes32 recursionLeafLevelVkHash;
        bytes32 recursionCircuitsSetVksHash;
    }
    /// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification.
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IVerifier {
        /// @dev Verifies a zk-SNARK proof.
        /// @return A boolean value indicating whether the zk-SNARK proof is valid.
        /// Note: The function may revert execution instead of returning false in some cases.
        function verify(uint256[] calldata _publicInputs, uint256[] calldata _proof) external view returns (bool);
        /// @notice Calculates a keccak256 hash of the runtime loaded verification keys.
        /// @return vkHash The keccak256 hash of the loaded verification keys.
        function verificationKeyHash() external view returns (bytes32);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {SlotOccupied, NotInitializedReentrancyGuard, Reentrancy} from "./L1ContractErrors.sol";
    /**
     * @custom:security-contact [email protected]
     * @dev Contract module that helps prevent reentrant calls to a function.
     *
     * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
     * available, which can be applied to functions to make sure there are no nested
     * (reentrant) calls to them.
     *
     * Note that because there is a single `nonReentrant` guard, functions marked as
     * `nonReentrant` may not call one another. This can be worked around by making
     * those functions `private`, and then adding `external` `nonReentrant` entry
     * points to them.
     *
     * TIP: If you would like to learn more about reentrancy and alternative ways
     * to protect against it, check out our blog post
     * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
     *
     * _Since v2.5.0:_ this module is now much more gas efficient, given net gas
     * metering changes introduced in the Istanbul hardfork.
     */
    abstract contract ReentrancyGuard {
        /// @dev Address of lock flag variable.
        /// @dev Flag is placed at random memory location to not interfere with Storage contract.
        // keccak256("ReentrancyGuard") - 1;
        uint256 private constant LOCK_FLAG_ADDRESS = 0x8e94fed44239eb2314ab7a406345e6c5a8f0ccedf3b600de3d004e672c33abf4;
        // solhint-disable-next-line max-line-length
        // https://github.com/OpenZeppelin/openzeppelin-contracts/blob/566a774222707e424896c0c390a84dc3c13bdcb2/contracts/security/ReentrancyGuard.sol
        // The values being non-zero value makes deployment a bit more expensive,
        // but in exchange the refund on every call to nonReentrant will be lower in
        // amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to
        // increase the likelihood of the full refund coming into effect.
        uint256 private constant _NOT_ENTERED = 1;
        uint256 private constant _ENTERED = 2;
        modifier reentrancyGuardInitializer() {
            _initializeReentrancyGuard();
            _;
        }
        function _initializeReentrancyGuard() private {
            uint256 lockSlotOldValue;
            // Storing an initial non-zero value makes deployment a bit more
            // expensive but in exchange every call to nonReentrant
            // will be cheaper.
            assembly {
                lockSlotOldValue := sload(LOCK_FLAG_ADDRESS)
                sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
            }
            // Check that storage slot for reentrancy guard is empty to rule out possibility of slot conflict
            if (lockSlotOldValue != 0) {
                revert SlotOccupied();
            }
        }
        /**
         * @dev Prevents a contract from calling itself, directly or indirectly.
         * Calling a `nonReentrant` function from another `nonReentrant`
         * function is not supported. It is possible to prevent this from happening
         * by making the `nonReentrant` function external, and make it call a
         * `private` function that does the actual work.
         */
        modifier nonReentrant() {
            uint256 _status;
            assembly {
                _status := sload(LOCK_FLAG_ADDRESS)
            }
            if (_status == 0) {
                revert NotInitializedReentrancyGuard();
            }
            // On the first call to nonReentrant, _NOT_ENTERED will be true
            if (_status != _NOT_ENTERED) {
                revert Reentrancy();
            }
            // Any calls to nonReentrant after this point will fail
            assembly {
                sstore(LOCK_FLAG_ADDRESS, _ENTERED)
            }
            _;
            // By storing the original value once again, a refund is triggered (see
            // https://eips.ethereum.org/EIPS/eip-2200)
            assembly {
                sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IBridgehub} from "../../bridgehub/IBridgehub.sol";
    import {IL1NativeTokenVault} from "../ntv/IL1NativeTokenVault.sol";
    import {IL1ERC20Bridge} from "./IL1ERC20Bridge.sol";
    /// @param chainId The chain ID of the transaction to check.
    /// @param l2BatchNumber The L2 batch number where the withdrawal was processed.
    /// @param l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
    /// @param l2sender The address of the message sender on L2 (base token system contract address or asset handler)
    /// @param l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent.
    /// @param message The L2 withdraw data, stored in an L2 -> L1 message.
    /// @param merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization.
    struct FinalizeL1DepositParams {
        uint256 chainId;
        uint256 l2BatchNumber;
        uint256 l2MessageIndex;
        address l2Sender;
        uint16 l2TxNumberInBatch;
        bytes message;
        bytes32[] merkleProof;
    }
    /// @title L1 Bridge contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IL1Nullifier {
        event BridgehubDepositFinalized(
            uint256 indexed chainId,
            bytes32 indexed txDataHash,
            bytes32 indexed l2DepositTxHash
        );
        function isWithdrawalFinalized(
            uint256 _chainId,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex
        ) external view returns (bool);
        function claimFailedDepositLegacyErc20Bridge(
            address _depositSender,
            address _l1Token,
            uint256 _amount,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof
        ) external;
        function claimFailedDeposit(
            uint256 _chainId,
            address _depositSender,
            address _l1Token,
            uint256 _amount,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof
        ) external;
        function finalizeDeposit(FinalizeL1DepositParams calldata _finalizeWithdrawalParams) external;
        function BRIDGE_HUB() external view returns (IBridgehub);
        function legacyBridge() external view returns (IL1ERC20Bridge);
        function depositHappened(uint256 _chainId, bytes32 _l2TxHash) external view returns (bytes32);
        function bridgehubConfirmL2TransactionForwarded(uint256 _chainId, bytes32 _txDataHash, bytes32 _txHash) external;
        function l1NativeTokenVault() external view returns (IL1NativeTokenVault);
        function setL1NativeTokenVault(IL1NativeTokenVault _nativeTokenVault) external;
        function setL1AssetRouter(address _l1AssetRouter) external;
        function chainBalance(uint256 _chainId, address _token) external view returns (uint256);
        function l2BridgeAddress(uint256 _chainId) external view returns (address);
        function transferTokenToNTV(address _token) external;
        function nullifyChainBalanceByNTV(uint256 _chainId, address _token) external;
        /// @dev Withdraw funds from the initiated deposit, that failed when finalizing on L2.
        /// @param _chainId The ZK chain id to which deposit was initiated.
        /// @param _depositSender The address of the entity that initiated the deposit.
        /// @param _assetId The unique identifier of the deposited L1 token.
        /// @param _assetData The encoded transfer data, which includes both the deposit amount and the address of the L2 receiver. Might include extra information.
        /// @param _l2TxHash The L2 transaction hash of the failed deposit finalization.
        /// @param _l2BatchNumber The L2 batch number where the deposit finalization was processed.
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
        /// @param _l2TxNumberInBatch The L2 transaction number in a batch, in which the log was sent.
        /// @param _merkleProof The Merkle proof of the processing L1 -> L2 transaction with deposit finalization.
        /// @dev Processes claims of failed deposit, whether they originated from the legacy bridge or the current system.
        function bridgeRecoverFailedTransfer(
            uint256 _chainId,
            address _depositSender,
            bytes32 _assetId,
            bytes memory _assetData,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof
        ) external;
        /// @notice Legacy function to finalize withdrawal via the same
        /// interface as the old L1SharedBridge.
        /// @dev Note, that we need to keep this interface, since the `L2AssetRouter`
        /// will continue returning the previous address as the `l1SharedBridge`. The value
        /// returned by it is used in the SDK for finalizing withdrawals.
        /// @param _chainId The chain ID of the transaction to check
        /// @param _l2BatchNumber The L2 batch number where the withdrawal was processed
        /// @param _l2MessageIndex The position in the L2 logs Merkle tree of the l2Log that was sent with the message
        /// @param _l2TxNumberInBatch The L2 transaction number in the batch, in which the log was sent
        /// @param _message The L2 withdraw data, stored in an L2 -> L1 message
        /// @param _merkleProof The Merkle proof of the inclusion L2 -> L1 message about withdrawal initialization
        function finalizeWithdrawal(
            uint256 _chainId,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes calldata _message,
            bytes32[] calldata _merkleProof
        ) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IAssetRouterBase} from "../asset-router/IAssetRouterBase.sol";
    /// @title Base Native token vault contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens
    interface INativeTokenVault {
        event BridgedTokenBeaconUpdated(address bridgedTokenBeacon, bytes32 bridgedTokenProxyBytecodeHash);
        /// @notice The Weth token address
        function WETH_TOKEN() external view returns (address);
        /// @notice The AssetRouter contract
        function ASSET_ROUTER() external view returns (IAssetRouterBase);
        /// @notice The chain ID of the L1 chain
        function L1_CHAIN_ID() external view returns (uint256);
        /// @notice Returns the chain ID of the origin chain for a given asset ID
        function originChainId(bytes32 assetId) external view returns (uint256);
        /// @notice Registers tokens within the NTV.
        /// @dev The goal is to allow bridging native tokens automatically, by registering them on the fly.
        /// @notice Allows the bridge to register a token address for the vault.
        /// @notice No access control is ok, since the bridging of tokens should be permissionless. This requires permissionless registration.
        function registerToken(address _l1Token) external;
        /// @notice Ensures that the native token is registered with the NTV.
        /// @dev This function is used to ensure that the token is registered with the NTV.
        function ensureTokenIsRegistered(address _nativeToken) external returns (bytes32);
        /// @notice Used to get the the ERC20 data for a token
        function getERC20Getters(address _token, uint256 _originChainId) external view returns (bytes memory);
        /// @notice Used to get the token address of an assetId
        function tokenAddress(bytes32 assetId) external view returns (address);
        /// @notice Used to get the assetId of a token
        function assetId(address token) external view returns (bytes32);
        /// @notice Used to get the expected bridged token address corresponding to its native counterpart
        function calculateCreate2TokenAddress(uint256 _originChainId, address _originToken) external view returns (address);
        /// @notice Tries to register a token from the provided `_burnData` and reverts if it is not possible.
        function tryRegisterTokenFromBurnData(bytes calldata _burnData, bytes32 _expectedAssetId) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IBridgehub} from "../../bridgehub/IBridgehub.sol";
    /// @dev The encoding version used for legacy txs.
    bytes1 constant LEGACY_ENCODING_VERSION = 0x00;
    /// @dev The encoding version used for new txs.
    bytes1 constant NEW_ENCODING_VERSION = 0x01;
    /// @dev The encoding version used for txs that set the asset handler on the counterpart contract.
    bytes1 constant SET_ASSET_HANDLER_COUNTERPART_ENCODING_VERSION = 0x02;
    /// @title L1 Bridge contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IAssetRouterBase {
        event BridgehubDepositBaseTokenInitiated(
            uint256 indexed chainId,
            address indexed from,
            bytes32 assetId,
            uint256 amount
        );
        event BridgehubDepositInitiated(
            uint256 indexed chainId,
            bytes32 indexed txDataHash,
            address indexed from,
            bytes32 assetId,
            bytes bridgeMintCalldata
        );
        event BridgehubWithdrawalInitiated(
            uint256 chainId,
            address indexed sender,
            bytes32 indexed assetId,
            bytes32 assetDataHash // Todo: What's the point of emitting hash?
        );
        event AssetDeploymentTrackerRegistered(
            bytes32 indexed assetId,
            bytes32 indexed additionalData,
            address assetDeploymentTracker
        );
        event AssetHandlerRegistered(bytes32 indexed assetId, address indexed _assetHandlerAddress);
        event DepositFinalizedAssetRouter(uint256 indexed chainId, bytes32 indexed assetId, bytes assetData);
        function BRIDGE_HUB() external view returns (IBridgehub);
        /// @notice Sets the asset handler address for a specified asset ID on the chain of the asset deployment tracker.
        /// @dev The caller of this function is encoded within the `assetId`, therefore, it should be invoked by the asset deployment tracker contract.
        /// @dev No access control on the caller, as msg.sender is encoded in the assetId.
        /// @dev Typically, for most tokens, ADT is the native token vault. However, custom tokens may have their own specific asset deployment trackers.
        /// @dev `setAssetHandlerAddressOnCounterpart` should be called on L1 to set asset handlers on L2 chains for a specific asset ID.
        /// @param _assetRegistrationData The asset data which may include the asset address and any additional required data or encodings.
        /// @param _assetHandlerAddress The address of the asset handler to be set for the provided asset.
        function setAssetHandlerAddressThisChain(bytes32 _assetRegistrationData, address _assetHandlerAddress) external;
        function assetHandlerAddress(bytes32 _assetId) external view returns (address);
        /// @notice Finalize the withdrawal and release funds.
        /// @param _chainId The chain ID of the transaction to check.
        /// @param _assetId The bridged asset ID.
        /// @param _transferData The position in the L2 logs Merkle tree of the l2Log that was sent with the message.
        /// @dev We have both the legacy finalizeWithdrawal and the new finalizeDeposit functions,
        /// finalizeDeposit uses the new format. On the L2 we have finalizeDeposit with new and old formats both.
        function finalizeDeposit(uint256 _chainId, bytes32 _assetId, bytes memory _transferData) external payable;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    /// @title L1 Bridge contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IL1SharedBridgeLegacy {
        function l2BridgeAddress(uint256 _chainId) external view returns (address);
    }
    // SPDX-License-Identifier: MIT
    // We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version.
    pragma solidity ^0.8.21;
    import {IL1Nullifier} from "./IL1Nullifier.sol";
    import {IL1NativeTokenVault} from "../ntv/IL1NativeTokenVault.sol";
    import {IL1AssetRouter} from "../asset-router/IL1AssetRouter.sol";
    /// @title L1 Bridge contract legacy interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice Legacy Bridge interface before ZK chain migration, used for backward compatibility with ZKsync Era
    interface IL1ERC20Bridge {
        event DepositInitiated(
            bytes32 indexed l2DepositTxHash,
            address indexed from,
            address indexed to,
            address l1Token,
            uint256 amount
        );
        event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount);
        event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount);
        function isWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool);
        function deposit(
            address _l2Receiver,
            address _l1Token,
            uint256 _amount,
            uint256 _l2TxGasLimit,
            uint256 _l2TxGasPerPubdataByte,
            address _refundRecipient
        ) external payable returns (bytes32 txHash);
        function deposit(
            address _l2Receiver,
            address _l1Token,
            uint256 _amount,
            uint256 _l2TxGasLimit,
            uint256 _l2TxGasPerPubdataByte
        ) external payable returns (bytes32 txHash);
        function claimFailedDeposit(
            address _depositSender,
            address _l1Token,
            bytes32 _l2TxHash,
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes32[] calldata _merkleProof
        ) external;
        function finalizeWithdrawal(
            uint256 _l2BatchNumber,
            uint256 _l2MessageIndex,
            uint16 _l2TxNumberInBatch,
            bytes calldata _message,
            bytes32[] calldata _merkleProof
        ) external;
        function l2TokenAddress(address _l1Token) external view returns (address);
        function L1_NULLIFIER() external view returns (IL1Nullifier);
        function L1_ASSET_ROUTER() external view returns (IL1AssetRouter);
        function L1_NATIVE_TOKEN_VAULT() external view returns (IL1NativeTokenVault);
        function l2TokenBeacon() external view returns (address);
        function l2Bridge() external view returns (address);
        function depositAmount(
            address _account,
            address _l1Token,
            bytes32 _depositL2TxHash
        ) external view returns (uint256 amount);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
    // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
     * checks.
     *
     * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
     * easily result in undesired exploitation or bugs, since developers usually
     * assume that overflows raise errors. `SafeCast` restores this intuition by
     * reverting the transaction when such an operation overflows.
     *
     * Using this library instead of the unchecked operations eliminates an entire
     * class of bugs, so it's recommended to use it always.
     *
     * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
     * all math on `uint256` and `int256` and then downcasting.
     */
    library SafeCast {
        /**
         * @dev Returns the downcasted uint248 from uint256, reverting on
         * overflow (when the input is greater than largest uint248).
         *
         * Counterpart to Solidity's `uint248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         *
         * _Available since v4.7._
         */
        function toUint248(uint256 value) internal pure returns (uint248) {
            require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
            return uint248(value);
        }
        /**
         * @dev Returns the downcasted uint240 from uint256, reverting on
         * overflow (when the input is greater than largest uint240).
         *
         * Counterpart to Solidity's `uint240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         *
         * _Available since v4.7._
         */
        function toUint240(uint256 value) internal pure returns (uint240) {
            require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
            return uint240(value);
        }
        /**
         * @dev Returns the downcasted uint232 from uint256, reverting on
         * overflow (when the input is greater than largest uint232).
         *
         * Counterpart to Solidity's `uint232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         *
         * _Available since v4.7._
         */
        function toUint232(uint256 value) internal pure returns (uint232) {
            require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
            return uint232(value);
        }
        /**
         * @dev Returns the downcasted uint224 from uint256, reverting on
         * overflow (when the input is greater than largest uint224).
         *
         * Counterpart to Solidity's `uint224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         *
         * _Available since v4.2._
         */
        function toUint224(uint256 value) internal pure returns (uint224) {
            require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
            return uint224(value);
        }
        /**
         * @dev Returns the downcasted uint216 from uint256, reverting on
         * overflow (when the input is greater than largest uint216).
         *
         * Counterpart to Solidity's `uint216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         *
         * _Available since v4.7._
         */
        function toUint216(uint256 value) internal pure returns (uint216) {
            require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
            return uint216(value);
        }
        /**
         * @dev Returns the downcasted uint208 from uint256, reverting on
         * overflow (when the input is greater than largest uint208).
         *
         * Counterpart to Solidity's `uint208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         *
         * _Available since v4.7._
         */
        function toUint208(uint256 value) internal pure returns (uint208) {
            require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
            return uint208(value);
        }
        /**
         * @dev Returns the downcasted uint200 from uint256, reverting on
         * overflow (when the input is greater than largest uint200).
         *
         * Counterpart to Solidity's `uint200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         *
         * _Available since v4.7._
         */
        function toUint200(uint256 value) internal pure returns (uint200) {
            require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
            return uint200(value);
        }
        /**
         * @dev Returns the downcasted uint192 from uint256, reverting on
         * overflow (when the input is greater than largest uint192).
         *
         * Counterpart to Solidity's `uint192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         *
         * _Available since v4.7._
         */
        function toUint192(uint256 value) internal pure returns (uint192) {
            require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
            return uint192(value);
        }
        /**
         * @dev Returns the downcasted uint184 from uint256, reverting on
         * overflow (when the input is greater than largest uint184).
         *
         * Counterpart to Solidity's `uint184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         *
         * _Available since v4.7._
         */
        function toUint184(uint256 value) internal pure returns (uint184) {
            require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
            return uint184(value);
        }
        /**
         * @dev Returns the downcasted uint176 from uint256, reverting on
         * overflow (when the input is greater than largest uint176).
         *
         * Counterpart to Solidity's `uint176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         *
         * _Available since v4.7._
         */
        function toUint176(uint256 value) internal pure returns (uint176) {
            require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
            return uint176(value);
        }
        /**
         * @dev Returns the downcasted uint168 from uint256, reverting on
         * overflow (when the input is greater than largest uint168).
         *
         * Counterpart to Solidity's `uint168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         *
         * _Available since v4.7._
         */
        function toUint168(uint256 value) internal pure returns (uint168) {
            require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
            return uint168(value);
        }
        /**
         * @dev Returns the downcasted uint160 from uint256, reverting on
         * overflow (when the input is greater than largest uint160).
         *
         * Counterpart to Solidity's `uint160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         *
         * _Available since v4.7._
         */
        function toUint160(uint256 value) internal pure returns (uint160) {
            require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
            return uint160(value);
        }
        /**
         * @dev Returns the downcasted uint152 from uint256, reverting on
         * overflow (when the input is greater than largest uint152).
         *
         * Counterpart to Solidity's `uint152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         *
         * _Available since v4.7._
         */
        function toUint152(uint256 value) internal pure returns (uint152) {
            require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
            return uint152(value);
        }
        /**
         * @dev Returns the downcasted uint144 from uint256, reverting on
         * overflow (when the input is greater than largest uint144).
         *
         * Counterpart to Solidity's `uint144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         *
         * _Available since v4.7._
         */
        function toUint144(uint256 value) internal pure returns (uint144) {
            require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
            return uint144(value);
        }
        /**
         * @dev Returns the downcasted uint136 from uint256, reverting on
         * overflow (when the input is greater than largest uint136).
         *
         * Counterpart to Solidity's `uint136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         *
         * _Available since v4.7._
         */
        function toUint136(uint256 value) internal pure returns (uint136) {
            require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
            return uint136(value);
        }
        /**
         * @dev Returns the downcasted uint128 from uint256, reverting on
         * overflow (when the input is greater than largest uint128).
         *
         * Counterpart to Solidity's `uint128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         *
         * _Available since v2.5._
         */
        function toUint128(uint256 value) internal pure returns (uint128) {
            require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
            return uint128(value);
        }
        /**
         * @dev Returns the downcasted uint120 from uint256, reverting on
         * overflow (when the input is greater than largest uint120).
         *
         * Counterpart to Solidity's `uint120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         *
         * _Available since v4.7._
         */
        function toUint120(uint256 value) internal pure returns (uint120) {
            require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
            return uint120(value);
        }
        /**
         * @dev Returns the downcasted uint112 from uint256, reverting on
         * overflow (when the input is greater than largest uint112).
         *
         * Counterpart to Solidity's `uint112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         *
         * _Available since v4.7._
         */
        function toUint112(uint256 value) internal pure returns (uint112) {
            require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
            return uint112(value);
        }
        /**
         * @dev Returns the downcasted uint104 from uint256, reverting on
         * overflow (when the input is greater than largest uint104).
         *
         * Counterpart to Solidity's `uint104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         *
         * _Available since v4.7._
         */
        function toUint104(uint256 value) internal pure returns (uint104) {
            require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
            return uint104(value);
        }
        /**
         * @dev Returns the downcasted uint96 from uint256, reverting on
         * overflow (when the input is greater than largest uint96).
         *
         * Counterpart to Solidity's `uint96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         *
         * _Available since v4.2._
         */
        function toUint96(uint256 value) internal pure returns (uint96) {
            require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
            return uint96(value);
        }
        /**
         * @dev Returns the downcasted uint88 from uint256, reverting on
         * overflow (when the input is greater than largest uint88).
         *
         * Counterpart to Solidity's `uint88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         *
         * _Available since v4.7._
         */
        function toUint88(uint256 value) internal pure returns (uint88) {
            require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
            return uint88(value);
        }
        /**
         * @dev Returns the downcasted uint80 from uint256, reverting on
         * overflow (when the input is greater than largest uint80).
         *
         * Counterpart to Solidity's `uint80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         *
         * _Available since v4.7._
         */
        function toUint80(uint256 value) internal pure returns (uint80) {
            require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
            return uint80(value);
        }
        /**
         * @dev Returns the downcasted uint72 from uint256, reverting on
         * overflow (when the input is greater than largest uint72).
         *
         * Counterpart to Solidity's `uint72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         *
         * _Available since v4.7._
         */
        function toUint72(uint256 value) internal pure returns (uint72) {
            require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
            return uint72(value);
        }
        /**
         * @dev Returns the downcasted uint64 from uint256, reverting on
         * overflow (when the input is greater than largest uint64).
         *
         * Counterpart to Solidity's `uint64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         *
         * _Available since v2.5._
         */
        function toUint64(uint256 value) internal pure returns (uint64) {
            require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
            return uint64(value);
        }
        /**
         * @dev Returns the downcasted uint56 from uint256, reverting on
         * overflow (when the input is greater than largest uint56).
         *
         * Counterpart to Solidity's `uint56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         *
         * _Available since v4.7._
         */
        function toUint56(uint256 value) internal pure returns (uint56) {
            require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
            return uint56(value);
        }
        /**
         * @dev Returns the downcasted uint48 from uint256, reverting on
         * overflow (when the input is greater than largest uint48).
         *
         * Counterpart to Solidity's `uint48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         *
         * _Available since v4.7._
         */
        function toUint48(uint256 value) internal pure returns (uint48) {
            require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
            return uint48(value);
        }
        /**
         * @dev Returns the downcasted uint40 from uint256, reverting on
         * overflow (when the input is greater than largest uint40).
         *
         * Counterpart to Solidity's `uint40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         *
         * _Available since v4.7._
         */
        function toUint40(uint256 value) internal pure returns (uint40) {
            require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
            return uint40(value);
        }
        /**
         * @dev Returns the downcasted uint32 from uint256, reverting on
         * overflow (when the input is greater than largest uint32).
         *
         * Counterpart to Solidity's `uint32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         *
         * _Available since v2.5._
         */
        function toUint32(uint256 value) internal pure returns (uint32) {
            require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
            return uint32(value);
        }
        /**
         * @dev Returns the downcasted uint24 from uint256, reverting on
         * overflow (when the input is greater than largest uint24).
         *
         * Counterpart to Solidity's `uint24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         *
         * _Available since v4.7._
         */
        function toUint24(uint256 value) internal pure returns (uint24) {
            require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
            return uint24(value);
        }
        /**
         * @dev Returns the downcasted uint16 from uint256, reverting on
         * overflow (when the input is greater than largest uint16).
         *
         * Counterpart to Solidity's `uint16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         *
         * _Available since v2.5._
         */
        function toUint16(uint256 value) internal pure returns (uint16) {
            require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
            return uint16(value);
        }
        /**
         * @dev Returns the downcasted uint8 from uint256, reverting on
         * overflow (when the input is greater than largest uint8).
         *
         * Counterpart to Solidity's `uint8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         *
         * _Available since v2.5._
         */
        function toUint8(uint256 value) internal pure returns (uint8) {
            require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
            return uint8(value);
        }
        /**
         * @dev Converts a signed int256 into an unsigned uint256.
         *
         * Requirements:
         *
         * - input must be greater than or equal to 0.
         *
         * _Available since v3.0._
         */
        function toUint256(int256 value) internal pure returns (uint256) {
            require(value >= 0, "SafeCast: value must be positive");
            return uint256(value);
        }
        /**
         * @dev Returns the downcasted int248 from int256, reverting on
         * overflow (when the input is less than smallest int248 or
         * greater than largest int248).
         *
         * Counterpart to Solidity's `int248` operator.
         *
         * Requirements:
         *
         * - input must fit into 248 bits
         *
         * _Available since v4.7._
         */
        function toInt248(int256 value) internal pure returns (int248 downcasted) {
            downcasted = int248(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
        }
        /**
         * @dev Returns the downcasted int240 from int256, reverting on
         * overflow (when the input is less than smallest int240 or
         * greater than largest int240).
         *
         * Counterpart to Solidity's `int240` operator.
         *
         * Requirements:
         *
         * - input must fit into 240 bits
         *
         * _Available since v4.7._
         */
        function toInt240(int256 value) internal pure returns (int240 downcasted) {
            downcasted = int240(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
        }
        /**
         * @dev Returns the downcasted int232 from int256, reverting on
         * overflow (when the input is less than smallest int232 or
         * greater than largest int232).
         *
         * Counterpart to Solidity's `int232` operator.
         *
         * Requirements:
         *
         * - input must fit into 232 bits
         *
         * _Available since v4.7._
         */
        function toInt232(int256 value) internal pure returns (int232 downcasted) {
            downcasted = int232(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
        }
        /**
         * @dev Returns the downcasted int224 from int256, reverting on
         * overflow (when the input is less than smallest int224 or
         * greater than largest int224).
         *
         * Counterpart to Solidity's `int224` operator.
         *
         * Requirements:
         *
         * - input must fit into 224 bits
         *
         * _Available since v4.7._
         */
        function toInt224(int256 value) internal pure returns (int224 downcasted) {
            downcasted = int224(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
        }
        /**
         * @dev Returns the downcasted int216 from int256, reverting on
         * overflow (when the input is less than smallest int216 or
         * greater than largest int216).
         *
         * Counterpart to Solidity's `int216` operator.
         *
         * Requirements:
         *
         * - input must fit into 216 bits
         *
         * _Available since v4.7._
         */
        function toInt216(int256 value) internal pure returns (int216 downcasted) {
            downcasted = int216(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
        }
        /**
         * @dev Returns the downcasted int208 from int256, reverting on
         * overflow (when the input is less than smallest int208 or
         * greater than largest int208).
         *
         * Counterpart to Solidity's `int208` operator.
         *
         * Requirements:
         *
         * - input must fit into 208 bits
         *
         * _Available since v4.7._
         */
        function toInt208(int256 value) internal pure returns (int208 downcasted) {
            downcasted = int208(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
        }
        /**
         * @dev Returns the downcasted int200 from int256, reverting on
         * overflow (when the input is less than smallest int200 or
         * greater than largest int200).
         *
         * Counterpart to Solidity's `int200` operator.
         *
         * Requirements:
         *
         * - input must fit into 200 bits
         *
         * _Available since v4.7._
         */
        function toInt200(int256 value) internal pure returns (int200 downcasted) {
            downcasted = int200(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
        }
        /**
         * @dev Returns the downcasted int192 from int256, reverting on
         * overflow (when the input is less than smallest int192 or
         * greater than largest int192).
         *
         * Counterpart to Solidity's `int192` operator.
         *
         * Requirements:
         *
         * - input must fit into 192 bits
         *
         * _Available since v4.7._
         */
        function toInt192(int256 value) internal pure returns (int192 downcasted) {
            downcasted = int192(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
        }
        /**
         * @dev Returns the downcasted int184 from int256, reverting on
         * overflow (when the input is less than smallest int184 or
         * greater than largest int184).
         *
         * Counterpart to Solidity's `int184` operator.
         *
         * Requirements:
         *
         * - input must fit into 184 bits
         *
         * _Available since v4.7._
         */
        function toInt184(int256 value) internal pure returns (int184 downcasted) {
            downcasted = int184(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
        }
        /**
         * @dev Returns the downcasted int176 from int256, reverting on
         * overflow (when the input is less than smallest int176 or
         * greater than largest int176).
         *
         * Counterpart to Solidity's `int176` operator.
         *
         * Requirements:
         *
         * - input must fit into 176 bits
         *
         * _Available since v4.7._
         */
        function toInt176(int256 value) internal pure returns (int176 downcasted) {
            downcasted = int176(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
        }
        /**
         * @dev Returns the downcasted int168 from int256, reverting on
         * overflow (when the input is less than smallest int168 or
         * greater than largest int168).
         *
         * Counterpart to Solidity's `int168` operator.
         *
         * Requirements:
         *
         * - input must fit into 168 bits
         *
         * _Available since v4.7._
         */
        function toInt168(int256 value) internal pure returns (int168 downcasted) {
            downcasted = int168(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
        }
        /**
         * @dev Returns the downcasted int160 from int256, reverting on
         * overflow (when the input is less than smallest int160 or
         * greater than largest int160).
         *
         * Counterpart to Solidity's `int160` operator.
         *
         * Requirements:
         *
         * - input must fit into 160 bits
         *
         * _Available since v4.7._
         */
        function toInt160(int256 value) internal pure returns (int160 downcasted) {
            downcasted = int160(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
        }
        /**
         * @dev Returns the downcasted int152 from int256, reverting on
         * overflow (when the input is less than smallest int152 or
         * greater than largest int152).
         *
         * Counterpart to Solidity's `int152` operator.
         *
         * Requirements:
         *
         * - input must fit into 152 bits
         *
         * _Available since v4.7._
         */
        function toInt152(int256 value) internal pure returns (int152 downcasted) {
            downcasted = int152(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
        }
        /**
         * @dev Returns the downcasted int144 from int256, reverting on
         * overflow (when the input is less than smallest int144 or
         * greater than largest int144).
         *
         * Counterpart to Solidity's `int144` operator.
         *
         * Requirements:
         *
         * - input must fit into 144 bits
         *
         * _Available since v4.7._
         */
        function toInt144(int256 value) internal pure returns (int144 downcasted) {
            downcasted = int144(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
        }
        /**
         * @dev Returns the downcasted int136 from int256, reverting on
         * overflow (when the input is less than smallest int136 or
         * greater than largest int136).
         *
         * Counterpart to Solidity's `int136` operator.
         *
         * Requirements:
         *
         * - input must fit into 136 bits
         *
         * _Available since v4.7._
         */
        function toInt136(int256 value) internal pure returns (int136 downcasted) {
            downcasted = int136(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
        }
        /**
         * @dev Returns the downcasted int128 from int256, reverting on
         * overflow (when the input is less than smallest int128 or
         * greater than largest int128).
         *
         * Counterpart to Solidity's `int128` operator.
         *
         * Requirements:
         *
         * - input must fit into 128 bits
         *
         * _Available since v3.1._
         */
        function toInt128(int256 value) internal pure returns (int128 downcasted) {
            downcasted = int128(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
        }
        /**
         * @dev Returns the downcasted int120 from int256, reverting on
         * overflow (when the input is less than smallest int120 or
         * greater than largest int120).
         *
         * Counterpart to Solidity's `int120` operator.
         *
         * Requirements:
         *
         * - input must fit into 120 bits
         *
         * _Available since v4.7._
         */
        function toInt120(int256 value) internal pure returns (int120 downcasted) {
            downcasted = int120(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
        }
        /**
         * @dev Returns the downcasted int112 from int256, reverting on
         * overflow (when the input is less than smallest int112 or
         * greater than largest int112).
         *
         * Counterpart to Solidity's `int112` operator.
         *
         * Requirements:
         *
         * - input must fit into 112 bits
         *
         * _Available since v4.7._
         */
        function toInt112(int256 value) internal pure returns (int112 downcasted) {
            downcasted = int112(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
        }
        /**
         * @dev Returns the downcasted int104 from int256, reverting on
         * overflow (when the input is less than smallest int104 or
         * greater than largest int104).
         *
         * Counterpart to Solidity's `int104` operator.
         *
         * Requirements:
         *
         * - input must fit into 104 bits
         *
         * _Available since v4.7._
         */
        function toInt104(int256 value) internal pure returns (int104 downcasted) {
            downcasted = int104(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
        }
        /**
         * @dev Returns the downcasted int96 from int256, reverting on
         * overflow (when the input is less than smallest int96 or
         * greater than largest int96).
         *
         * Counterpart to Solidity's `int96` operator.
         *
         * Requirements:
         *
         * - input must fit into 96 bits
         *
         * _Available since v4.7._
         */
        function toInt96(int256 value) internal pure returns (int96 downcasted) {
            downcasted = int96(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
        }
        /**
         * @dev Returns the downcasted int88 from int256, reverting on
         * overflow (when the input is less than smallest int88 or
         * greater than largest int88).
         *
         * Counterpart to Solidity's `int88` operator.
         *
         * Requirements:
         *
         * - input must fit into 88 bits
         *
         * _Available since v4.7._
         */
        function toInt88(int256 value) internal pure returns (int88 downcasted) {
            downcasted = int88(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
        }
        /**
         * @dev Returns the downcasted int80 from int256, reverting on
         * overflow (when the input is less than smallest int80 or
         * greater than largest int80).
         *
         * Counterpart to Solidity's `int80` operator.
         *
         * Requirements:
         *
         * - input must fit into 80 bits
         *
         * _Available since v4.7._
         */
        function toInt80(int256 value) internal pure returns (int80 downcasted) {
            downcasted = int80(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
        }
        /**
         * @dev Returns the downcasted int72 from int256, reverting on
         * overflow (when the input is less than smallest int72 or
         * greater than largest int72).
         *
         * Counterpart to Solidity's `int72` operator.
         *
         * Requirements:
         *
         * - input must fit into 72 bits
         *
         * _Available since v4.7._
         */
        function toInt72(int256 value) internal pure returns (int72 downcasted) {
            downcasted = int72(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
        }
        /**
         * @dev Returns the downcasted int64 from int256, reverting on
         * overflow (when the input is less than smallest int64 or
         * greater than largest int64).
         *
         * Counterpart to Solidity's `int64` operator.
         *
         * Requirements:
         *
         * - input must fit into 64 bits
         *
         * _Available since v3.1._
         */
        function toInt64(int256 value) internal pure returns (int64 downcasted) {
            downcasted = int64(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
        }
        /**
         * @dev Returns the downcasted int56 from int256, reverting on
         * overflow (when the input is less than smallest int56 or
         * greater than largest int56).
         *
         * Counterpart to Solidity's `int56` operator.
         *
         * Requirements:
         *
         * - input must fit into 56 bits
         *
         * _Available since v4.7._
         */
        function toInt56(int256 value) internal pure returns (int56 downcasted) {
            downcasted = int56(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
        }
        /**
         * @dev Returns the downcasted int48 from int256, reverting on
         * overflow (when the input is less than smallest int48 or
         * greater than largest int48).
         *
         * Counterpart to Solidity's `int48` operator.
         *
         * Requirements:
         *
         * - input must fit into 48 bits
         *
         * _Available since v4.7._
         */
        function toInt48(int256 value) internal pure returns (int48 downcasted) {
            downcasted = int48(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
        }
        /**
         * @dev Returns the downcasted int40 from int256, reverting on
         * overflow (when the input is less than smallest int40 or
         * greater than largest int40).
         *
         * Counterpart to Solidity's `int40` operator.
         *
         * Requirements:
         *
         * - input must fit into 40 bits
         *
         * _Available since v4.7._
         */
        function toInt40(int256 value) internal pure returns (int40 downcasted) {
            downcasted = int40(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
        }
        /**
         * @dev Returns the downcasted int32 from int256, reverting on
         * overflow (when the input is less than smallest int32 or
         * greater than largest int32).
         *
         * Counterpart to Solidity's `int32` operator.
         *
         * Requirements:
         *
         * - input must fit into 32 bits
         *
         * _Available since v3.1._
         */
        function toInt32(int256 value) internal pure returns (int32 downcasted) {
            downcasted = int32(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
        }
        /**
         * @dev Returns the downcasted int24 from int256, reverting on
         * overflow (when the input is less than smallest int24 or
         * greater than largest int24).
         *
         * Counterpart to Solidity's `int24` operator.
         *
         * Requirements:
         *
         * - input must fit into 24 bits
         *
         * _Available since v4.7._
         */
        function toInt24(int256 value) internal pure returns (int24 downcasted) {
            downcasted = int24(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
        }
        /**
         * @dev Returns the downcasted int16 from int256, reverting on
         * overflow (when the input is less than smallest int16 or
         * greater than largest int16).
         *
         * Counterpart to Solidity's `int16` operator.
         *
         * Requirements:
         *
         * - input must fit into 16 bits
         *
         * _Available since v3.1._
         */
        function toInt16(int256 value) internal pure returns (int16 downcasted) {
            downcasted = int16(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
        }
        /**
         * @dev Returns the downcasted int8 from int256, reverting on
         * overflow (when the input is less than smallest int8 or
         * greater than largest int8).
         *
         * Counterpart to Solidity's `int8` operator.
         *
         * Requirements:
         *
         * - input must fit into 8 bits
         *
         * _Available since v3.1._
         */
        function toInt8(int256 value) internal pure returns (int8 downcasted) {
            downcasted = int8(value);
            require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
        }
        /**
         * @dev Converts an unsigned uint256 into a signed int256.
         *
         * Requirements:
         *
         * - input must be less than or equal to maxInt256.
         *
         * _Available since v3.0._
         */
        function toInt256(uint256 value) internal pure returns (int256) {
            // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
            require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
            return int256(value);
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    interface IL1AssetDeploymentTracker {
        function bridgeCheckCounterpartAddress(
            uint256 _chainId,
            bytes32 _assetId,
            address _originalCaller,
            address _assetHandlerAddressOnCounterpart
        ) external view;
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/Arrays.sol)
    pragma solidity ^0.8.0;
    import "./StorageSlot.sol";
    import "./math/Math.sol";
    /**
     * @dev Collection of functions related to array types.
     */
    library Arrays {
        using StorageSlot for bytes32;
        /**
         * @dev Searches a sorted `array` and returns the first index that contains
         * a value greater or equal to `element`. If no such index exists (i.e. all
         * values in the array are strictly less than `element`), the array length is
         * returned. Time complexity O(log n).
         *
         * `array` is expected to be sorted in ascending order, and to contain no
         * repeated elements.
         */
        function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
            if (array.length == 0) {
                return 0;
            }
            uint256 low = 0;
            uint256 high = array.length;
            while (low < high) {
                uint256 mid = Math.average(low, high);
                // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
                // because Math.average rounds down (it does integer division with truncation).
                if (unsafeAccess(array, mid).value > element) {
                    high = mid;
                } else {
                    low = mid + 1;
                }
            }
            // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
            if (low > 0 && unsafeAccess(array, low - 1).value == element) {
                return low - 1;
            } else {
                return low;
            }
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getAddressSlot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getBytes32Slot();
        }
        /**
         * @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
         *
         * WARNING: Only use if you are certain `pos` is lower than the array length.
         */
        function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
            bytes32 slot;
            // We use assembly to calculate the storage slot of the element at index `pos` of the dynamic array `arr`
            // following https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html#mappings-and-dynamic-arrays.
            /// @solidity memory-safe-assembly
            assembly {
                mstore(0, arr.slot)
                slot := add(keccak256(0, 0x20), pos)
            }
            return slot.getUint256Slot();
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.24;
    import {IL1Nullifier} from "../interfaces/IL1Nullifier.sol";
    import {INativeTokenVault} from "./INativeTokenVault.sol";
    import {IL1AssetDeploymentTracker} from "../interfaces/IL1AssetDeploymentTracker.sol";
    /// @title L1 Native token vault contract interface
    /// @author Matter Labs
    /// @custom:security-contact [email protected]
    /// @notice The NTV is an Asset Handler for the L1AssetRouter to handle native tokens
    // is IL1AssetHandler, IL1BaseTokenAssetHandler {
    interface IL1NativeTokenVault is INativeTokenVault, IL1AssetDeploymentTracker {
        /// @notice The L1Nullifier contract
        function L1_NULLIFIER() external view returns (IL1Nullifier);
        /// @notice Returns the total number of specific tokens locked for some chain
        function chainBalance(uint256 _chainId, bytes32 _assetId) external view returns (uint256);
        /// @notice Registers ETH token
        function registerEthToken() external;
        event TokenBeaconUpdated(address indexed l2TokenBeacon);
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
    // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
    pragma solidity ^0.8.0;
    /**
     * @dev Library for reading and writing primitive types to specific storage slots.
     *
     * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
     * This library helps with reading and writing to such slots without the need for inline assembly.
     *
     * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
     *
     * Example usage to set ERC1967 implementation slot:
     * ```solidity
     * contract ERC1967 {
     *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
     *
     *     function _getImplementation() internal view returns (address) {
     *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
     *     }
     *
     *     function _setImplementation(address newImplementation) internal {
     *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
     *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
     *     }
     * }
     * ```
     *
     * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
     * _Available since v4.9 for `string`, `bytes`._
     */
    library StorageSlot {
        struct AddressSlot {
            address value;
        }
        struct BooleanSlot {
            bool value;
        }
        struct Bytes32Slot {
            bytes32 value;
        }
        struct Uint256Slot {
            uint256 value;
        }
        struct StringSlot {
            string value;
        }
        struct BytesSlot {
            bytes value;
        }
        /**
         * @dev Returns an `AddressSlot` with member `value` located at `slot`.
         */
        function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
         */
        function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
         */
        function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
         */
        function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` with member `value` located at `slot`.
         */
        function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
         */
        function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` with member `value` located at `slot`.
         */
        function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := slot
            }
        }
        /**
         * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
         */
        function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
            /// @solidity memory-safe-assembly
            assembly {
                r.slot := store.slot
            }
        }
    }