Transaction Hash:
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 | ||
---|---|---|---|---|---|
0x6Adb3baB...16393C200
Miner
| (MEV Builder: 0x6adb...200) | 5.012775134533227358 Eth | 5.012777607504242373 Eth | 0.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
File 2 of 2: MailboxFacet
// 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 } } }