Contract Name:
ExecutorFacet
Contract Source Code:
// 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
// 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;
// TODO: change constant to the real root hash of empty Merkle tree (SMA-184)
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 = 32;
/// @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;
address constant ETH_TOKEN_ADDRESS = address(1);
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));
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
// 0x1ff9d522
error AddressAlreadyUsed(address addr);
// 0x86bb51b8
error AddressHasNoCode(address);
// 0x1eee5481
error AddressTooLow(address);
// 0x6afd6c20
error BadReturnData();
// 0x6ef9a972
error BaseTokenGasPriceDenominatorNotSet();
// 0x55ad3fd3
error BatchHashMismatch(bytes32 expected, bytes32 actual);
// 0x2078a6a0
error BatchNotExecuted(uint256 batchNumber);
// 0xbd4455ff
error BatchNumberMismatch(uint256 expectedBatchNumber, uint256 providedBatchNumber);
// 0xafd53e2f
error BlobHashCommitmentError(uint256 index, bool blobHashEmpty, bool blobCommitmentEmpty);
// 0x6cf12312
error BridgeHubAlreadyRegistered();
// 0xcf102c5a
error CalldataLengthTooBig();
// 0xe85392f9
error CanOnlyProcessOneBatch();
// 0x00c6ead2
error CantExecuteUnprovenBatches();
// 0xe18cb383
error CantRevertExecutedBatch();
// 0x78d2ed02
error ChainAlreadyLive();
// 0x8f620a06
error ChainIdTooBig();
// 0xf7a01e4d
error DelegateCallFailed(bytes returnData);
// 0x0a8ed92c
error DenominatorIsZero();
// 0xc7c9660f
error DepositDoesNotExist();
// 0xad2fa98e
error DepositExists();
// 0x79cacff1
error DepositFailed();
// 0xae08e4af
error DepositIncorrectAmount(uint256 expectedAmt, uint256 providedAmt);
// 0x0e7ee319
error DiamondAlreadyFrozen();
// 0x682dabb4
error DiamondFreezeIncorrectState();
// 0xa7151b9a
error DiamondNotFrozen();
// 0xfc7ab1d3
error EmptyBlobVersionHash(uint256 index);
// 0x95b66fe9
error EmptyDeposit();
// 0xac4a3f98
error FacetExists(bytes4 selector, address);
// 0x79e12cc3
error FacetIsFrozen(bytes4 func);
// 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);
// 0xb615c2b1
error HyperchainLimitReached();
// 0x826fb11e
error InsufficientChainBalance();
// 0x356680b7
error InsufficientFunds();
// 0x7a47c9a2
error InvalidChainId();
// 0x4fbe5dba
error InvalidDelay();
// 0x0af806e0
error InvalidHash();
// 0xc1780bd6
error InvalidLogSender(address sender, uint256 logKey);
// 0xd8e9405c
error InvalidNumberOfBlobs(uint256 expected, uint256 numCommitments, uint256 numHashes);
// 0x09bde339
error InvalidProof();
// 0x5428eae7
error InvalidProtocolVersion();
// 0x53e6d04d
error InvalidPubdataCommitmentsSize();
// 0x5513177c
error InvalidPubdataHash(bytes32 expectedHash, bytes32 provided);
// 0x9094af7e
error InvalidPubdataLength();
// 0xc5d09071
error InvalidPubdataMode();
// 0x6f1cf752
error InvalidPubdataPricingMode();
// 0x12ba286f
error InvalidSelector(bytes4 func);
// 0x5cb29523
error InvalidTxType(uint256 txType);
// 0x5f1aa154
error InvalidUpgradeTxn(UpgradeTxVerifyParam);
// 0xaa7feadc
error InvalidValue();
// 0xa4f62e33
error L2BridgeNotDeployed(uint256 chainId);
// 0xff8811ff
error L2BridgeNotSet(uint256 chainId);
// 0xcb5e4247
error L2BytecodeHashMismatch(bytes32 expected, bytes32 provided);
// 0xfb5c22e6
error L2TimestampTooBig();
// 0xd2c011d6
error L2UpgradeNonceNotEqualToNewProtocolVersion(uint256 nonce, uint256 protocolVersion);
// 0x97e1359e
error L2WithdrawalMessageWrongLength(uint256 messageLen);
// 0x32eb8b2f
error LegacyMethodIsSupportedOnlyForEra();
// 0xe37d2c02
error LengthIsNotDivisibleBy32(uint256 length);
// 0x1b6825bb
error LogAlreadyProcessed(uint8);
// 0x43e266b0
error MalformedBytecode(BytecodeError);
// 0x59170bf0
error MalformedCalldata();
// 0x16509b9a
error MalformedMessage();
// 0x9bb54c35
error MerkleIndexOutOfBounds();
// 0x8e23ac1a
error MerklePathEmpty();
// 0x1c500385
error MerklePathOutOfBounds();
// 0xfa44b527
error MissingSystemLogs(uint256 expected, uint256 actual);
// 0x4a094431
error MsgValueMismatch(uint256 expectedMsgValue, uint256 providedMsgValue);
// 0xb385a3da
error MsgValueTooLow(uint256 required, uint256 provided);
// 0x72ea85ad
error NewProtocolMajorVersionNotZero();
// 0x79cc2d22
error NoCallsProvided();
// 0xa6fef710
error NoFunctionsForDiamondCut();
// 0xcab098d8
error NoFundsTransferred();
// 0x92290acc
error NonEmptyBlobVersionHash(uint256 index);
// 0xc21b1ab7
error NonEmptyCalldata();
// 0x536ec84b
error NonEmptyMsgValue();
// 0xd018e08e
error NonIncreasingTimestamp();
// 0x0105f9c0
error NonSequentialBatch();
// 0x4ef79e5a
error NonZeroAddress(address);
// 0xdd629f86
error NotEnoughGas();
// 0xdd7e3621
error NotInitializedReentrancyGuard();
// 0xf3ed9dfa
error OnlyEraSupported();
// 0x1a21feed
error OperationExists();
// 0xeda2fbb1
error OperationMustBePending();
// 0xe1c1ff37
error OperationMustBeReady();
// 0xd7f50a9d
error PatchCantSetUpgradeTxn();
// 0x962fd7d0
error PatchUpgradeCantSetBootloader();
// 0x559cc34e
error PatchUpgradeCantSetDefaultAccount();
// 0x8d5851de
error PointEvalCallFailed(bytes);
// 0x4daa985d
error PointEvalFailed(bytes);
// 0x9b48e060
error PreviousOperationNotExecuted();
// 0x5c598b60
error PreviousProtocolMajorVersionNotZero();
// 0xa0f47245
error PreviousUpgradeNotCleaned();
// 0x101ba748
error PreviousUpgradeNotFinalized(bytes32 txHash);
// 0xd5a99014
error PriorityOperationsRollingHashMismatch();
// 0x1a4d284a
error PriorityTxPubdataExceedsMaxPubDataPerBatch();
// 0xa461f651
error ProtocolIdMismatch(uint256 expectedProtocolVersion, uint256 providedProtocolId);
// 0x64f94ec2
error ProtocolIdNotGreater();
// 0xd328c12a
error ProtocolVersionMinorDeltaTooBig(uint256 limit, uint256 proposed);
// 0x88d7b498
error ProtocolVersionTooSmall();
// 0x53dee67b
error PubdataCommitmentsEmpty();
// 0x7734c31a
error PubdataCommitmentsTooBig();
// 0x959f26fb
error PubdataGreaterThanLimit(uint256 limit, uint256 length);
// 0x2a4a14df
error PubdataPerBatchIsLessThanTxn();
// 0x63c36549
error QueueIsEmpty();
// 0xab143c06
error Reentrancy();
// 0x667d17de
error RemoveFunctionFacetAddressNotZero(address facet);
// 0xa2d4b16c
error RemoveFunctionFacetAddressZero();
// 0x3580370c
error ReplaceFunctionFacetAddressZero();
// 0xdab52f4b
error RevertedBatchBeforeNewBatch();
// 0x9a67c1cb
error RevertedBatchNotAfterNewLastBatch();
// 0xd3b6535b
error SelectorsMustAllHaveSameFreezability();
// 0x7774d2f9
error SharedBridgeValueNotSet(SharedBridgeKey);
// 0xc1d9246c
error SharedBridgeBalanceMismatch();
// 0x856d5b77
error SharedBridgeNotSet();
// 0xcac5fc40
error SharedBridgeValueAlreadySet(SharedBridgeKey);
// 0xdf3a8fdd
error SlotOccupied();
// 0xd0bc70cf
error STMAlreadyRegistered();
// 0x09865e10
error STMNotRegistered();
// 0xae43b424
error SystemLogsSizeTooBig();
// 0x08753982
error TimeNotReached(uint256 expectedTimestamp, uint256 actualTimestamp);
// 0x2d50c33b
error TimestampError();
// 0x4f4b634e
error TokenAlreadyRegistered(address token);
// 0xddef98d7
error TokenNotRegistered(address token);
// 0x06439c6b
error TokenNotSupported(address token);
// 0x23830e28
error TokensWithFeesNotSupported();
// 0xf640f0e5
error TooManyBlobs();
// 0x76da24b9
error TooManyFactoryDeps();
// 0xf0b4e88f
error TooMuchGas();
// 0x00c5a6a9
error TransactionNotAllowed();
// 0x4c991078
error TxHashMismatch();
// 0x2e311df8
error TxnBodyGasLimitNotEnoughGas();
// 0x8e4a23d6
error Unauthorized(address caller);
// 0xe52478c7
error UndefinedDiamondCutAction();
// 0x07218375
error UnexpectedNumberOfFactoryDeps();
// 0x6aa39880
error UnexpectedSystemLog(uint256 logKey);
// 0xf093c2e5
error UpgradeBatchNumberIsNotZero();
// 0x47b3b145
error ValidateTxnNotEnoughGas();
// 0x626ade30
error ValueMismatch(uint256 expected, uint256 actual);
// 0xe1022469
error VerifiedBatchesExceedsCommittedBatches();
// 0x2dbdba00
error VerifyProofCommittedVerifiedMismatch();
// 0xae899454
error WithdrawalAlreadyFinalized();
// 0x27fcd9d1
error WithdrawalFailed();
// 0x750b219c
error WithdrawFailed();
// 0x15e8e429
error WrongMagicValue(uint256 expectedMagicValue, uint256 providedMagicValue);
// 0xd92e233d
error ZeroAddress();
// 0x669567ea
error ZeroBalance();
// 0xc84885d4
error ZeroChainId();
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
// 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 formal address of the initial program of the system: the bootloader
address constant L2_BOOTLOADER_ADDRESS = address(0x8001);
/// @dev The address of the known code storage system contract
address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004);
/// @dev The address of the L2 deployer system contract.
address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006);
/// @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(0x8007);
/// @dev The address of the special smart contract that can send arbitrary length message as an L2 log
address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008);
/// @dev The address of the eth token system contract
address constant L2_BASE_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a);
/// @dev The address of the context system contract
address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b);
/// @dev The address of the pubdata chunk publisher contract
address constant L2_PUBDATA_CHUNK_PUBLISHER_ADDR = address(0x8011);
// 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;
/**
* @author Matter Labs
* @custom:security-contact [email protected]
* @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array.
* @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type.
*
* @dev WARNING!
* 1) Functions don't check the length of the bytes array, so it can go out of bounds.
* The user of the library must check for bytes length before using any functions from the library!
*
* 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html.
* Using data in inline assembly can lead to unexpected behavior!
*/
library UnsafeBytes {
function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) {
assembly {
offset := add(_start, 4)
result := mload(add(_bytes, offset))
}
}
function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) {
assembly {
offset := add(_start, 20)
result := mload(add(_bytes, offset))
}
}
function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) {
assembly {
offset := add(_start, 32)
result := mload(add(_bytes, offset))
}
}
function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) {
assembly {
offset := add(_start, 32)
result := mload(add(_bytes, offset))
}
}
}
// 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
// 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 {ZkSyncHyperchainBase} from "./ZkSyncHyperchainBase.sol";
import {COMMIT_TIMESTAMP_NOT_OLDER, COMMIT_TIMESTAMP_APPROXIMATION_DELTA, EMPTY_STRING_KECCAK, L2_TO_L1_LOG_SERIALIZE_SIZE, MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, PACKED_L2_BLOCK_TIMESTAMP_MASK, PUBLIC_INPUT_SHIFT, POINT_EVALUATION_PRECOMPILE_ADDR} from "../../../common/Config.sol";
import {IExecutor, L2_LOG_ADDRESS_OFFSET, L2_LOG_KEY_OFFSET, L2_LOG_VALUE_OFFSET, SystemLogKey, LogProcessingOutput, PubdataSource, BLS_MODULUS, PUBDATA_COMMITMENT_SIZE, PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET, PUBDATA_COMMITMENT_COMMITMENT_OFFSET, MAX_NUMBER_OF_BLOBS, TOTAL_BLOBS_IN_COMMITMENT, BLOB_SIZE_BYTES} from "../../chain-interfaces/IExecutor.sol";
import {PriorityQueue, PriorityOperation} from "../../libraries/PriorityQueue.sol";
import {UncheckedMath} from "../../../common/libraries/UncheckedMath.sol";
import {UnsafeBytes} from "../../../common/libraries/UnsafeBytes.sol";
import {L2_BOOTLOADER_ADDRESS, L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR, L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR, L2_PUBDATA_CHUNK_PUBLISHER_ADDR} from "../../../common/L2ContractAddresses.sol";
import {PubdataPricingMode} from "../ZkSyncHyperchainStorage.sol";
import {IStateTransitionManager} from "../../IStateTransitionManager.sol";
import {BatchNumberMismatch, TimeNotReached, TooManyBlobs, ValueMismatch, InvalidPubdataMode, InvalidPubdataLength, HashMismatch, NonIncreasingTimestamp, TimestampError, InvalidLogSender, TxHashMismatch, UnexpectedSystemLog, MissingSystemLogs, LogAlreadyProcessed, InvalidProtocolVersion, CanOnlyProcessOneBatch, BatchHashMismatch, UpgradeBatchNumberIsNotZero, NonSequentialBatch, CantExecuteUnprovenBatches, SystemLogsSizeTooBig, InvalidNumberOfBlobs, VerifiedBatchesExceedsCommittedBatches, InvalidProof, RevertedBatchNotAfterNewLastBatch, CantRevertExecutedBatch, PointEvalFailed, EmptyBlobVersionHash, NonEmptyBlobVersionHash, BlobHashCommitmentError, CalldataLengthTooBig, InvalidPubdataHash, L2TimestampTooBig, PriorityOperationsRollingHashMismatch, PubdataCommitmentsEmpty, PointEvalCallFailed, PubdataCommitmentsTooBig, InvalidPubdataCommitmentsSize} from "../../../common/L1ContractErrors.sol";
// While formally the following import is not used, it is needed to inherit documentation from it
import {IZkSyncHyperchainBase} from "../../chain-interfaces/IZkSyncHyperchainBase.sol";
/// @title ZKsync hyperchain Executor contract capable of processing events emitted in the ZKsync hyperchain protocol.
/// @author Matter Labs
/// @custom:security-contact [email protected]
contract ExecutorFacet is ZkSyncHyperchainBase, IExecutor {
using UncheckedMath for uint256;
using PriorityQueue for PriorityQueue.Queue;
/// @inheritdoc IZkSyncHyperchainBase
string public constant override getName = "ExecutorFacet";
/// @dev Process one batch commit using the previous batch StoredBatchInfo
/// @dev returns new batch StoredBatchInfo
/// @notice Does not change storage
function _commitOneBatch(
StoredBatchInfo memory _previousBatch,
CommitBatchInfo calldata _newBatch,
bytes32 _expectedSystemContractUpgradeTxHash
) internal view returns (StoredBatchInfo memory) {
// only commit next batch
if (_newBatch.batchNumber != _previousBatch.batchNumber + 1) {
revert BatchNumberMismatch(_previousBatch.batchNumber + 1, _newBatch.batchNumber);
}
uint8 pubdataSource = uint8(bytes1(_newBatch.pubdataCommitments[0]));
PubdataPricingMode pricingMode = s.feeParams.pubdataPricingMode;
if (
pricingMode != PubdataPricingMode.Validium &&
pubdataSource != uint8(PubdataSource.Calldata) &&
pubdataSource != uint8(PubdataSource.Blob)
) {
revert InvalidPubdataMode();
}
// Check that batch contain all meta information for L2 logs.
// Get the chained hash of priority transaction hashes.
LogProcessingOutput memory logOutput = _processL2Logs(_newBatch, _expectedSystemContractUpgradeTxHash);
bytes32[] memory blobCommitments = new bytes32[](MAX_NUMBER_OF_BLOBS);
if (pricingMode == PubdataPricingMode.Validium) {
// skipping data validation for validium, we just check that the data is empty
if (_newBatch.pubdataCommitments.length != 1) {
revert CalldataLengthTooBig();
}
for (uint8 i = uint8(SystemLogKey.BLOB_ONE_HASH_KEY); i <= uint8(SystemLogKey.BLOB_SIX_HASH_KEY); ++i) {
logOutput.blobHashes[i - uint8(SystemLogKey.BLOB_ONE_HASH_KEY)] = bytes32(0);
}
} else if (pubdataSource == uint8(PubdataSource.Blob)) {
// In this scenario, pubdataCommitments is a list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes
blobCommitments = _verifyBlobInformation(_newBatch.pubdataCommitments[1:], logOutput.blobHashes);
} else if (pubdataSource == uint8(PubdataSource.Calldata)) {
// In this scenario pubdataCommitments is actual pubdata consisting of l2 to l1 logs, l2 to l1 message, compressed smart contract bytecode, and compressed state diffs
if (_newBatch.pubdataCommitments.length > BLOB_SIZE_BYTES) {
revert InvalidPubdataLength();
}
bytes32 pubdataHash = keccak256(_newBatch.pubdataCommitments[1:_newBatch.pubdataCommitments.length - 32]);
if (logOutput.pubdataHash != pubdataHash) {
revert InvalidPubdataHash(pubdataHash, logOutput.pubdataHash);
}
blobCommitments[0] = bytes32(
_newBatch.pubdataCommitments[_newBatch.pubdataCommitments.length - 32:_newBatch
.pubdataCommitments
.length]
);
}
if (_previousBatch.batchHash != logOutput.previousBatchHash) {
revert HashMismatch(logOutput.previousBatchHash, _previousBatch.batchHash);
}
// Check that the priority operation hash in the L2 logs is as expected
if (logOutput.chainedPriorityTxsHash != _newBatch.priorityOperationsHash) {
revert HashMismatch(logOutput.chainedPriorityTxsHash, _newBatch.priorityOperationsHash);
}
// Check that the number of processed priority operations is as expected
if (logOutput.numberOfLayer1Txs != _newBatch.numberOfLayer1Txs) {
revert ValueMismatch(logOutput.numberOfLayer1Txs, _newBatch.numberOfLayer1Txs);
}
// Check the timestamp of the new batch
_verifyBatchTimestamp(logOutput.packedBatchAndL2BlockTimestamp, _newBatch.timestamp, _previousBatch.timestamp);
// Create batch commitment for the proof verification
bytes32 commitment = _createBatchCommitment(
_newBatch,
logOutput.stateDiffHash,
blobCommitments,
logOutput.blobHashes
);
return
StoredBatchInfo({
batchNumber: _newBatch.batchNumber,
batchHash: _newBatch.newStateRoot,
indexRepeatedStorageChanges: _newBatch.indexRepeatedStorageChanges,
numberOfLayer1Txs: _newBatch.numberOfLayer1Txs,
priorityOperationsHash: _newBatch.priorityOperationsHash,
l2LogsTreeRoot: logOutput.l2LogsTreeRoot,
timestamp: _newBatch.timestamp,
commitment: commitment
});
}
/// @notice checks that the timestamps of both the new batch and the new L2 block are correct.
/// @param _packedBatchAndL2BlockTimestamp - packed batch and L2 block timestamp in a format of batchTimestamp * 2**128 + l2BatchTimestamp
/// @param _expectedBatchTimestamp - expected batch timestamp
/// @param _previousBatchTimestamp - the timestamp of the previous batch
function _verifyBatchTimestamp(
uint256 _packedBatchAndL2BlockTimestamp,
uint256 _expectedBatchTimestamp,
uint256 _previousBatchTimestamp
) internal view {
// Check that the timestamp that came from the system context is expected
uint256 batchTimestamp = _packedBatchAndL2BlockTimestamp >> 128;
if (batchTimestamp != _expectedBatchTimestamp) {
revert TimestampError();
}
// While the fact that _previousBatchTimestamp < batchTimestamp is already checked on L2,
// we double check it here for clarity
if (_previousBatchTimestamp >= batchTimestamp) {
revert NonIncreasingTimestamp();
}
uint256 lastL2BlockTimestamp = _packedBatchAndL2BlockTimestamp & PACKED_L2_BLOCK_TIMESTAMP_MASK;
// All L2 blocks have timestamps within the range of [batchTimestamp, lastL2BlockTimestamp].
// So here we need to only double check that:
// - The timestamp of the batch is not too small.
// - The timestamp of the last L2 block is not too big.
// New batch timestamp is too small
if (block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER > batchTimestamp) {
revert TimeNotReached(batchTimestamp, block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER);
}
// The last L2 block timestamp is too big
if (lastL2BlockTimestamp > block.timestamp + COMMIT_TIMESTAMP_APPROXIMATION_DELTA) {
revert L2TimestampTooBig();
}
}
/// @dev Check that L2 logs are proper and batch contain all meta information for them
/// @dev The logs processed here should line up such that only one log for each key from the
/// SystemLogKey enum in Constants.sol is processed per new batch.
/// @dev Data returned from here will be used to form the batch commitment.
function _processL2Logs(
CommitBatchInfo calldata _newBatch,
bytes32 _expectedSystemContractUpgradeTxHash
) internal pure returns (LogProcessingOutput memory logOutput) {
// Copy L2 to L1 logs into memory.
bytes memory emittedL2Logs = _newBatch.systemLogs;
logOutput.blobHashes = new bytes32[](MAX_NUMBER_OF_BLOBS);
// Used as bitmap to set/check log processing happens exactly once.
// See SystemLogKey enum in Constants.sol for ordering.
uint256 processedLogs = 0;
// linear traversal of the logs
uint256 logsLength = emittedL2Logs.length;
for (uint256 i = 0; i < logsLength; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {
// Extract the values to be compared to/used such as the log sender, key, and value
// slither-disable-next-line unused-return
(address logSender, ) = UnsafeBytes.readAddress(emittedL2Logs, i + L2_LOG_ADDRESS_OFFSET);
// slither-disable-next-line unused-return
(uint256 logKey, ) = UnsafeBytes.readUint256(emittedL2Logs, i + L2_LOG_KEY_OFFSET);
// slither-disable-next-line unused-return
(bytes32 logValue, ) = UnsafeBytes.readBytes32(emittedL2Logs, i + L2_LOG_VALUE_OFFSET);
// Ensure that the log hasn't been processed already
if (_checkBit(processedLogs, uint8(logKey))) {
revert LogAlreadyProcessed(uint8(logKey));
}
processedLogs = _setBit(processedLogs, uint8(logKey));
// Need to check that each log was sent by the correct address.
if (logKey == uint256(SystemLogKey.L2_TO_L1_LOGS_TREE_ROOT_KEY)) {
if (logSender != L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.l2LogsTreeRoot = logValue;
} else if (logKey == uint256(SystemLogKey.TOTAL_L2_TO_L1_PUBDATA_KEY)) {
if (logSender != L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.pubdataHash = logValue;
} else if (logKey == uint256(SystemLogKey.STATE_DIFF_HASH_KEY)) {
if (logSender != L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.stateDiffHash = logValue;
} else if (logKey == uint256(SystemLogKey.PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY)) {
if (logSender != L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.packedBatchAndL2BlockTimestamp = uint256(logValue);
} else if (logKey == uint256(SystemLogKey.PREV_BATCH_HASH_KEY)) {
if (logSender != L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.previousBatchHash = logValue;
} else if (logKey == uint256(SystemLogKey.CHAINED_PRIORITY_TXN_HASH_KEY)) {
if (logSender != L2_BOOTLOADER_ADDRESS) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.chainedPriorityTxsHash = logValue;
} else if (logKey == uint256(SystemLogKey.NUMBER_OF_LAYER_1_TXS_KEY)) {
if (logSender != L2_BOOTLOADER_ADDRESS) {
revert InvalidLogSender(logSender, logKey);
}
logOutput.numberOfLayer1Txs = uint256(logValue);
} else if (
logKey >= uint256(SystemLogKey.BLOB_ONE_HASH_KEY) && logKey <= uint256(SystemLogKey.BLOB_SIX_HASH_KEY)
) {
if (logSender != L2_PUBDATA_CHUNK_PUBLISHER_ADDR) {
revert InvalidLogSender(logSender, logKey);
}
uint8 blobNumber = uint8(logKey) - uint8(SystemLogKey.BLOB_ONE_HASH_KEY);
if (blobNumber >= MAX_NUMBER_OF_BLOBS) {
revert TooManyBlobs();
}
logOutput.blobHashes[blobNumber] = logValue;
} else if (logKey == uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY)) {
if (logSender != L2_BOOTLOADER_ADDRESS) {
revert InvalidLogSender(logSender, logKey);
}
if (_expectedSystemContractUpgradeTxHash != logValue) {
revert TxHashMismatch();
}
} else if (logKey > uint256(SystemLogKey.EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY)) {
revert UnexpectedSystemLog(logKey);
}
}
// We only require 13 logs to be checked, the 14th is if we are expecting a protocol upgrade
// Without the protocol upgrade we expect 13 logs: 2^13 - 1 = 8191
// With the protocol upgrade we expect 14 logs: 2^14 - 1 = 16383
if (_expectedSystemContractUpgradeTxHash == bytes32(0)) {
if (processedLogs != 8191) {
revert MissingSystemLogs(8191, processedLogs);
}
} else {
if (processedLogs != 16383) {
revert MissingSystemLogs(16383, processedLogs);
}
}
}
/// @inheritdoc IExecutor
function commitBatches(
StoredBatchInfo calldata _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) external nonReentrant onlyValidator {
_commitBatches(_lastCommittedBatchData, _newBatchesData);
}
/// @inheritdoc IExecutor
function commitBatchesSharedBridge(
uint256, // _chainId
StoredBatchInfo calldata _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) external nonReentrant onlyValidator {
_commitBatches(_lastCommittedBatchData, _newBatchesData);
}
function _commitBatches(
StoredBatchInfo memory _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) internal {
// check that we have the right protocol version
// three comments:
// 1. A chain has to keep their protocol version up to date, as processing a block requires the latest or previous protocol version
// to solve this we will need to add the feature to create batches with only the protocol upgrade tx, without any other txs.
// 2. A chain might become out of sync if it launches while we are in the middle of a protocol upgrade. This would mean they cannot process their genesis upgrade
// as their protocolversion would be outdated, and they also cannot process the protocol upgrade tx as they have a pending upgrade.
// 3. The protocol upgrade is increased in the BaseZkSyncUpgrade, in the executor only the systemContractsUpgradeTxHash is checked
if (!IStateTransitionManager(s.stateTransitionManager).protocolVersionIsActive(s.protocolVersion)) {
revert InvalidProtocolVersion();
}
// With the new changes for EIP-4844, namely the restriction on number of blobs per block, we only allow for a single batch to be committed at a time.
if (_newBatchesData.length != 1) {
revert CanOnlyProcessOneBatch();
}
// Check that we commit batches after last committed batch
if (s.storedBatchHashes[s.totalBatchesCommitted] != _hashStoredBatchInfo(_lastCommittedBatchData)) {
// incorrect previous batch data
revert BatchHashMismatch(
s.storedBatchHashes[s.totalBatchesCommitted],
_hashStoredBatchInfo(_lastCommittedBatchData)
);
}
bytes32 systemContractsUpgradeTxHash = s.l2SystemContractsUpgradeTxHash;
// Upgrades are rarely done so we optimize a case with no active system contracts upgrade.
if (systemContractsUpgradeTxHash == bytes32(0) || s.l2SystemContractsUpgradeBatchNumber != 0) {
_commitBatchesWithoutSystemContractsUpgrade(_lastCommittedBatchData, _newBatchesData);
} else {
_commitBatchesWithSystemContractsUpgrade(
_lastCommittedBatchData,
_newBatchesData,
systemContractsUpgradeTxHash
);
}
s.totalBatchesCommitted = s.totalBatchesCommitted + _newBatchesData.length;
}
/// @dev Commits new batches without any system contracts upgrade.
/// @param _lastCommittedBatchData The data of the last committed batch.
/// @param _newBatchesData An array of batch data that needs to be committed.
function _commitBatchesWithoutSystemContractsUpgrade(
StoredBatchInfo memory _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) internal {
// We disable this check because calldata array length is cheap.
// solhint-disable-next-line gas-length-in-loops
for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) {
_lastCommittedBatchData = _commitOneBatch(_lastCommittedBatchData, _newBatchesData[i], bytes32(0));
s.storedBatchHashes[_lastCommittedBatchData.batchNumber] = _hashStoredBatchInfo(_lastCommittedBatchData);
emit BlockCommit(
_lastCommittedBatchData.batchNumber,
_lastCommittedBatchData.batchHash,
_lastCommittedBatchData.commitment
);
}
}
/// @dev Commits new batches with a system contracts upgrade transaction.
/// @param _lastCommittedBatchData The data of the last committed batch.
/// @param _newBatchesData An array of batch data that needs to be committed.
/// @param _systemContractUpgradeTxHash The transaction hash of the system contract upgrade.
function _commitBatchesWithSystemContractsUpgrade(
StoredBatchInfo memory _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData,
bytes32 _systemContractUpgradeTxHash
) internal {
// The system contract upgrade is designed to be executed atomically with the new bootloader, a default account,
// ZKP verifier, and other system parameters. Hence, we ensure that the upgrade transaction is
// carried out within the first batch committed after the upgrade.
// While the logic of the contract ensures that the s.l2SystemContractsUpgradeBatchNumber is 0 when this function is called,
// this check is added just in case. Since it is a hot read, it does not incur noticeable gas cost.
if (s.l2SystemContractsUpgradeBatchNumber != 0) {
revert UpgradeBatchNumberIsNotZero();
}
// Save the batch number where the upgrade transaction was executed.
s.l2SystemContractsUpgradeBatchNumber = _newBatchesData[0].batchNumber;
// We disable this check because calldata array length is cheap.
// solhint-disable-next-line gas-length-in-loops
for (uint256 i = 0; i < _newBatchesData.length; i = i.uncheckedInc()) {
// The upgrade transaction must only be included in the first batch.
bytes32 expectedUpgradeTxHash = i == 0 ? _systemContractUpgradeTxHash : bytes32(0);
_lastCommittedBatchData = _commitOneBatch(
_lastCommittedBatchData,
_newBatchesData[i],
expectedUpgradeTxHash
);
s.storedBatchHashes[_lastCommittedBatchData.batchNumber] = _hashStoredBatchInfo(_lastCommittedBatchData);
emit BlockCommit(
_lastCommittedBatchData.batchNumber,
_lastCommittedBatchData.batchHash,
_lastCommittedBatchData.commitment
);
}
}
/// @dev Pops the priority operations from the priority queue and returns a rolling hash of operations
function _collectOperationsFromPriorityQueue(uint256 _nPriorityOps) internal returns (bytes32 concatHash) {
concatHash = EMPTY_STRING_KECCAK;
for (uint256 i = 0; i < _nPriorityOps; i = i.uncheckedInc()) {
PriorityOperation memory priorityOp = s.priorityQueue.popFront();
concatHash = keccak256(abi.encode(concatHash, priorityOp.canonicalTxHash));
}
}
/// @dev Executes one batch
/// @dev 1. Processes all pending operations (Complete priority requests)
/// @dev 2. Finalizes batch on Ethereum
/// @dev _executedBatchIdx is an index in the array of the batches that we want to execute together
function _executeOneBatch(StoredBatchInfo memory _storedBatch, uint256 _executedBatchIdx) internal {
uint256 currentBatchNumber = _storedBatch.batchNumber;
if (currentBatchNumber != s.totalBatchesExecuted + _executedBatchIdx + 1) {
revert NonSequentialBatch();
}
if (_hashStoredBatchInfo(_storedBatch) != s.storedBatchHashes[currentBatchNumber]) {
revert BatchHashMismatch(s.storedBatchHashes[currentBatchNumber], _hashStoredBatchInfo(_storedBatch));
}
bytes32 priorityOperationsHash = _collectOperationsFromPriorityQueue(_storedBatch.numberOfLayer1Txs);
if (priorityOperationsHash != _storedBatch.priorityOperationsHash) {
revert PriorityOperationsRollingHashMismatch();
}
// Save root hash of L2 -> L1 logs tree
s.l2LogsRootHashes[currentBatchNumber] = _storedBatch.l2LogsTreeRoot;
}
/// @inheritdoc IExecutor
function executeBatchesSharedBridge(
uint256,
StoredBatchInfo[] calldata _batchesData
) external nonReentrant onlyValidator {
_executeBatches(_batchesData);
}
/// @inheritdoc IExecutor
function executeBatches(StoredBatchInfo[] calldata _batchesData) external nonReentrant onlyValidator {
_executeBatches(_batchesData);
}
function _executeBatches(StoredBatchInfo[] calldata _batchesData) internal {
uint256 nBatches = _batchesData.length;
for (uint256 i = 0; i < nBatches; i = i.uncheckedInc()) {
_executeOneBatch(_batchesData[i], i);
emit BlockExecution(_batchesData[i].batchNumber, _batchesData[i].batchHash, _batchesData[i].commitment);
}
uint256 newTotalBatchesExecuted = s.totalBatchesExecuted + nBatches;
s.totalBatchesExecuted = newTotalBatchesExecuted;
if (newTotalBatchesExecuted > s.totalBatchesVerified) {
revert CantExecuteUnprovenBatches();
}
uint256 batchWhenUpgradeHappened = s.l2SystemContractsUpgradeBatchNumber;
if (batchWhenUpgradeHappened != 0 && batchWhenUpgradeHappened <= newTotalBatchesExecuted) {
delete s.l2SystemContractsUpgradeTxHash;
delete s.l2SystemContractsUpgradeBatchNumber;
}
}
/// @inheritdoc IExecutor
function proveBatches(
StoredBatchInfo calldata _prevBatch,
StoredBatchInfo[] calldata _committedBatches,
ProofInput calldata _proof
) external nonReentrant onlyValidator {
_proveBatches(_prevBatch, _committedBatches, _proof);
}
/// @inheritdoc IExecutor
function proveBatchesSharedBridge(
uint256, // _chainId
StoredBatchInfo calldata _prevBatch,
StoredBatchInfo[] calldata _committedBatches,
ProofInput calldata _proof
) external nonReentrant onlyValidator {
_proveBatches(_prevBatch, _committedBatches, _proof);
}
function _proveBatches(
StoredBatchInfo calldata _prevBatch,
StoredBatchInfo[] calldata _committedBatches,
ProofInput calldata _proof
) internal {
// Save the variables into the stack to save gas on reading them later
uint256 currentTotalBatchesVerified = s.totalBatchesVerified;
uint256 committedBatchesLength = _committedBatches.length;
// Initialize the array, that will be used as public input to the ZKP
uint256[] memory proofPublicInput = new uint256[](committedBatchesLength);
// Check that the batch passed by the validator is indeed the first unverified batch
if (_hashStoredBatchInfo(_prevBatch) != s.storedBatchHashes[currentTotalBatchesVerified]) {
revert BatchHashMismatch(
s.storedBatchHashes[currentTotalBatchesVerified],
_hashStoredBatchInfo(_prevBatch)
);
}
bytes32 prevBatchCommitment = _prevBatch.commitment;
for (uint256 i = 0; i < committedBatchesLength; i = i.uncheckedInc()) {
currentTotalBatchesVerified = currentTotalBatchesVerified.uncheckedInc();
if (_hashStoredBatchInfo(_committedBatches[i]) != s.storedBatchHashes[currentTotalBatchesVerified]) {
revert BatchHashMismatch(
s.storedBatchHashes[currentTotalBatchesVerified],
_hashStoredBatchInfo(_committedBatches[i])
);
}
bytes32 currentBatchCommitment = _committedBatches[i].commitment;
proofPublicInput[i] = _getBatchProofPublicInput(prevBatchCommitment, currentBatchCommitment);
prevBatchCommitment = currentBatchCommitment;
}
if (currentTotalBatchesVerified > s.totalBatchesCommitted) {
revert VerifiedBatchesExceedsCommittedBatches();
}
_verifyProof(proofPublicInput, _proof);
emit BlocksVerification(s.totalBatchesVerified, currentTotalBatchesVerified);
s.totalBatchesVerified = currentTotalBatchesVerified;
}
function _verifyProof(uint256[] memory proofPublicInput, ProofInput calldata _proof) internal view {
// We can only process 1 batch proof at a time.
if (proofPublicInput.length != 1) {
revert CanOnlyProcessOneBatch();
}
bool successVerifyProof = s.verifier.verify(
proofPublicInput,
_proof.serializedProof,
_proof.recursiveAggregationInput
);
if (!successVerifyProof) {
revert InvalidProof();
}
}
/// @dev Gets zk proof public input
function _getBatchProofPublicInput(
bytes32 _prevBatchCommitment,
bytes32 _currentBatchCommitment
) internal pure returns (uint256) {
return
uint256(keccak256(abi.encodePacked(_prevBatchCommitment, _currentBatchCommitment))) >> PUBLIC_INPUT_SHIFT;
}
/// @inheritdoc IExecutor
function revertBatches(uint256 _newLastBatch) external nonReentrant onlyValidatorOrStateTransitionManager {
_revertBatches(_newLastBatch);
}
/// @inheritdoc IExecutor
function revertBatchesSharedBridge(uint256, uint256 _newLastBatch) external nonReentrant onlyValidator {
_revertBatches(_newLastBatch);
}
function _revertBatches(uint256 _newLastBatch) internal {
if (s.totalBatchesCommitted <= _newLastBatch) {
revert RevertedBatchNotAfterNewLastBatch();
}
if (_newLastBatch < s.totalBatchesExecuted) {
revert CantRevertExecutedBatch();
}
if (_newLastBatch < s.totalBatchesVerified) {
s.totalBatchesVerified = _newLastBatch;
}
s.totalBatchesCommitted = _newLastBatch;
// Reset the batch number of the executed system contracts upgrade transaction if the batch
// where the system contracts upgrade was committed is among the reverted batches.
if (s.l2SystemContractsUpgradeBatchNumber > _newLastBatch) {
delete s.l2SystemContractsUpgradeBatchNumber;
}
emit BlocksRevert(s.totalBatchesCommitted, s.totalBatchesVerified, s.totalBatchesExecuted);
}
/// @dev Creates batch commitment from its data
function _createBatchCommitment(
CommitBatchInfo calldata _newBatchData,
bytes32 _stateDiffHash,
bytes32[] memory _blobCommitments,
bytes32[] memory _blobHashes
) internal view returns (bytes32) {
bytes32 passThroughDataHash = keccak256(_batchPassThroughData(_newBatchData));
bytes32 metadataHash = keccak256(_batchMetaParameters());
bytes32 auxiliaryOutputHash = keccak256(
_batchAuxiliaryOutput(_newBatchData, _stateDiffHash, _blobCommitments, _blobHashes)
);
return keccak256(abi.encode(passThroughDataHash, metadataHash, auxiliaryOutputHash));
}
function _batchPassThroughData(CommitBatchInfo calldata _batch) internal pure returns (bytes memory) {
return
abi.encodePacked(
// solhint-disable-next-line func-named-parameters
_batch.indexRepeatedStorageChanges,
_batch.newStateRoot,
uint64(0), // index repeated storage changes in zkPorter
bytes32(0) // zkPorter batch hash
);
}
function _batchMetaParameters() internal view returns (bytes memory) {
bytes32 l2DefaultAccountBytecodeHash = s.l2DefaultAccountBytecodeHash;
return
abi.encodePacked(
s.zkPorterIsAvailable,
s.l2BootloaderBytecodeHash,
l2DefaultAccountBytecodeHash,
// VM 1.5.0 requires us to pass the EVM simulator code hash. For now it is the same as the default account.
l2DefaultAccountBytecodeHash
);
}
function _batchAuxiliaryOutput(
CommitBatchInfo calldata _batch,
bytes32 _stateDiffHash,
bytes32[] memory _blobCommitments,
bytes32[] memory _blobHashes
) internal pure returns (bytes memory) {
if (_batch.systemLogs.length > MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES) {
revert SystemLogsSizeTooBig();
}
bytes32 l2ToL1LogsHash = keccak256(_batch.systemLogs);
return
// solhint-disable-next-line func-named-parameters
abi.encodePacked(
l2ToL1LogsHash,
_stateDiffHash,
_batch.bootloaderHeapInitialContentsHash,
_batch.eventsQueueStateHash,
_encodeBlobAuxiliaryOutput(_blobCommitments, _blobHashes)
);
}
/// @dev Encodes the commitment to blobs to be used in the auxiliary output of the batch commitment
/// @param _blobCommitments - the commitments to the blobs
/// @param _blobHashes - the hashes of the blobs
/// @param blobAuxOutputWords - The circuit commitment to the blobs split into 32-byte words
function _encodeBlobAuxiliaryOutput(
bytes32[] memory _blobCommitments,
bytes32[] memory _blobHashes
) internal pure returns (bytes32[] memory blobAuxOutputWords) {
// These invariants should be checked by the caller of this function, but we double check
// just in case.
if (_blobCommitments.length != MAX_NUMBER_OF_BLOBS || _blobHashes.length != MAX_NUMBER_OF_BLOBS) {
revert InvalidNumberOfBlobs(MAX_NUMBER_OF_BLOBS, _blobCommitments.length, _blobHashes.length);
}
// for each blob we have:
// linear hash (hash of preimage from system logs) and
// output hash of blob commitments: keccak(versioned hash || opening point || evaluation value)
// These values will all be bytes32(0) when we submit pubdata via calldata instead of blobs.
//
// For now, only up to 6 blobs are supported by the contract, while 16 are required by the circuits.
// All the unfilled blobs will have their commitment as 0, including the case when we use only 1 blob.
blobAuxOutputWords = new bytes32[](2 * TOTAL_BLOBS_IN_COMMITMENT);
for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; ++i) {
blobAuxOutputWords[i * 2] = _blobHashes[i];
blobAuxOutputWords[i * 2 + 1] = _blobCommitments[i];
}
}
/// @notice Returns the keccak hash of the ABI-encoded StoredBatchInfo
function _hashStoredBatchInfo(StoredBatchInfo memory _storedBatchInfo) internal pure returns (bytes32) {
return keccak256(abi.encode(_storedBatchInfo));
}
/// @notice Returns true if the bit at index {_index} is 1
function _checkBit(uint256 _bitMap, uint8 _index) internal pure returns (bool) {
return (_bitMap & (1 << _index)) > 0;
}
/// @notice Sets the given bit in {_num} at index {_index} to 1.
function _setBit(uint256 _bitMap, uint8 _index) internal pure returns (uint256) {
return _bitMap | (1 << _index);
}
/// @notice Calls the point evaluation precompile and verifies the output
/// Verify p(z) = y given commitment that corresponds to the polynomial p(x) and a KZG proof.
/// Also verify that the provided commitment matches the provided versioned_hash.
///
function _pointEvaluationPrecompile(
bytes32 _versionedHash,
bytes32 _openingPoint,
bytes calldata _openingValueCommitmentProof
) internal view {
bytes memory precompileInput = abi.encodePacked(_versionedHash, _openingPoint, _openingValueCommitmentProof);
(bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall(precompileInput);
// We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the
// response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile
if (!success) {
revert PointEvalCallFailed(precompileInput);
}
(, uint256 result) = abi.decode(data, (uint256, uint256));
if (result != BLS_MODULUS) {
revert PointEvalFailed(abi.encode(result));
}
}
/// @dev Verifies that the blobs contain the correct data by calling the point evaluation precompile. For the precompile we need:
/// versioned hash || opening point || opening value || commitment || proof
/// the _pubdataCommitments will contain the last 4 values, the versioned hash is pulled from the BLOBHASH opcode
/// pubdataCommitments is a list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes
function _verifyBlobInformation(
bytes calldata _pubdataCommitments,
bytes32[] memory _blobHashes
) internal view returns (bytes32[] memory blobCommitments) {
uint256 versionedHashIndex = 0;
if (_pubdataCommitments.length == 0) {
revert PubdataCommitmentsEmpty();
}
if (_pubdataCommitments.length > PUBDATA_COMMITMENT_SIZE * MAX_NUMBER_OF_BLOBS) {
revert PubdataCommitmentsTooBig();
}
if (_pubdataCommitments.length % PUBDATA_COMMITMENT_SIZE != 0) {
revert InvalidPubdataCommitmentsSize();
}
blobCommitments = new bytes32[](MAX_NUMBER_OF_BLOBS);
// We disable this check because calldata array length is cheap.
// solhint-disable-next-line gas-length-in-loops
for (uint256 i = 0; i < _pubdataCommitments.length; i += PUBDATA_COMMITMENT_SIZE) {
bytes32 blobVersionedHash = _getBlobVersionedHash(versionedHashIndex);
if (blobVersionedHash == bytes32(0)) {
revert EmptyBlobVersionHash(versionedHashIndex);
}
// First 16 bytes is the opening point. While we get the point as 16 bytes, the point evaluation precompile
// requires it to be 32 bytes. The blob commitment must use the opening point as 16 bytes though.
bytes32 openingPoint = bytes32(
uint256(uint128(bytes16(_pubdataCommitments[i:i + PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET])))
);
_pointEvaluationPrecompile(
blobVersionedHash,
openingPoint,
_pubdataCommitments[i + PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET:i + PUBDATA_COMMITMENT_SIZE]
);
// Take the hash of the versioned hash || opening point || claimed value
blobCommitments[versionedHashIndex] = keccak256(
abi.encodePacked(blobVersionedHash, _pubdataCommitments[i:i + PUBDATA_COMMITMENT_COMMITMENT_OFFSET])
);
++versionedHashIndex;
}
// This check is required because we want to ensure that there aren't any extra blobs trying to be published.
// Calling the BLOBHASH opcode with an index > # blobs - 1 yields bytes32(0)
bytes32 versionedHash = _getBlobVersionedHash(versionedHashIndex);
if (versionedHash != bytes32(0)) {
revert NonEmptyBlobVersionHash(versionedHashIndex);
}
// We verify that for each set of blobHash/blobCommitment are either both empty
// or there are values for both.
for (uint256 i = 0; i < MAX_NUMBER_OF_BLOBS; ++i) {
if (
(_blobHashes[i] == bytes32(0) && blobCommitments[i] != bytes32(0)) ||
(_blobHashes[i] != bytes32(0) && blobCommitments[i] == bytes32(0))
) {
revert BlobHashCommitmentError(i, _blobHashes[i] == bytes32(0), blobCommitments[i] == bytes32(0));
}
}
}
function _getBlobVersionedHash(uint256 _index) internal view virtual returns (bytes32 versionedHash) {
assembly {
versionedHash := blobhash(_index)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {ZkSyncHyperchainStorage} from "../ZkSyncHyperchainStorage.sol";
import {ReentrancyGuard} from "../../../common/ReentrancyGuard.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 ZkSyncHyperchainBase is ReentrancyGuard {
// slither-disable-next-line uninitialized-state
ZkSyncHyperchainStorage 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 onlyStateTransitionManager() {
if (msg.sender != s.stateTransitionManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyBridgehub() {
if (msg.sender != s.bridgehub) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyAdminOrStateTransitionManager() {
if (msg.sender != s.admin && msg.sender != s.stateTransitionManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyValidatorOrStateTransitionManager() {
if (!s.validators[msg.sender] && msg.sender != s.stateTransitionManager) {
revert Unauthorized(msg.sender);
}
_;
}
modifier onlyBaseTokenBridge() {
if (msg.sender != s.baseTokenBridge) {
revert Unauthorized(msg.sender);
}
_;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {IVerifier, VerifierParams} from "../chain-interfaces/IVerifier.sol";
import {PriorityQueue} from "../../state-transition/libraries/PriorityQueue.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 hyperchain 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 ZkSyncHyperchainStorage {
/// @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 STM
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.
FeeParams feeParams;
/// @dev Address of the blob versioned hash getter smart contract used for EIP-4844 versioned hashes.
address blobVersionedHashRetriever;
/// @dev The chainId of the chain
uint256 chainId;
/// @dev The address of the bridgehub
address bridgehub;
/// @dev The address of the StateTransitionManager
address stateTransitionManager;
/// @dev The address of the baseToken contract. Eth is address(1)
address baseToken;
/// @dev The address of the baseTokenbridge. Eth also uses the shared bridge
address 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;
}
// 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 {IZkSyncHyperchainBase} from "./IZkSyncHyperchainBase.sol";
/// @dev Enum used by L2 System Contracts to differentiate logs.
enum SystemLogKey {
L2_TO_L1_LOGS_TREE_ROOT_KEY,
TOTAL_L2_TO_L1_PUBDATA_KEY,
STATE_DIFF_HASH_KEY,
PACKED_BATCH_AND_L2_BLOCK_TIMESTAMP_KEY,
PREV_BATCH_HASH_KEY,
CHAINED_PRIORITY_TXN_HASH_KEY,
NUMBER_OF_LAYER_1_TXS_KEY,
BLOB_ONE_HASH_KEY,
BLOB_TWO_HASH_KEY,
BLOB_THREE_HASH_KEY,
BLOB_FOUR_HASH_KEY,
BLOB_FIVE_HASH_KEY,
BLOB_SIX_HASH_KEY,
EXPECTED_SYSTEM_CONTRACT_UPGRADE_TX_HASH_KEY
}
/// @dev Enum used to determine the source of pubdata. At first we will support calldata and blobs but this can be extended.
enum PubdataSource {
Calldata,
Blob
}
struct LogProcessingOutput {
uint256 numberOfLayer1Txs;
bytes32 chainedPriorityTxsHash;
bytes32 previousBatchHash;
bytes32 pubdataHash;
bytes32 stateDiffHash;
bytes32 l2LogsTreeRoot;
uint256 packedBatchAndL2BlockTimestamp;
bytes32[] blobHashes;
}
/// @dev Total number of bytes in a blob. Blob = 4096 field elements * 31 bytes per field element
/// @dev EIP-4844 defines it as 131_072 but we use 4096 * 31 within our circuits to always fit within a field element
/// @dev Our circuits will prove that a EIP-4844 blob and our internal blob are the same.
uint256 constant BLOB_SIZE_BYTES = 126_976;
/// @dev Offset used to pull Address From Log. Equal to 4 (bytes for isService)
uint256 constant L2_LOG_ADDRESS_OFFSET = 4;
/// @dev Offset used to pull Key From Log. Equal to 4 (bytes for isService) + 20 (bytes for address)
uint256 constant L2_LOG_KEY_OFFSET = 24;
/// @dev Offset used to pull Value From Log. Equal to 4 (bytes for isService) + 20 (bytes for address) + 32 (bytes for key)
uint256 constant L2_LOG_VALUE_OFFSET = 56;
/// @dev BLS Modulus value defined in EIP-4844 and the magic value returned from a successful call to the
/// point evaluation precompile
uint256 constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513;
/// @dev Packed pubdata commitments.
/// @dev Format: list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes)) = 144 bytes
uint256 constant PUBDATA_COMMITMENT_SIZE = 144;
/// @dev Offset in pubdata commitment of blobs for claimed value
uint256 constant PUBDATA_COMMITMENT_CLAIMED_VALUE_OFFSET = 16;
/// @dev Offset in pubdata commitment of blobs for kzg commitment
uint256 constant PUBDATA_COMMITMENT_COMMITMENT_OFFSET = 48;
/// @dev Max number of blobs currently supported
uint256 constant MAX_NUMBER_OF_BLOBS = 6;
/// @dev The number of blobs that must be present in the commitment to a batch.
/// It represents the maximal number of blobs that circuits can support and can be larger
/// than the maximal number of blobs supported by the contract (`MAX_NUMBER_OF_BLOBS`).
uint256 constant TOTAL_BLOBS_IN_COMMITMENT = 16;
/// @title The interface of the ZKsync Executor contract capable of processing events emitted in the ZKsync protocol.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IExecutor is IZkSyncHyperchainBase {
/// @notice Rollup batch stored data
/// @param batchNumber Rollup batch number
/// @param batchHash Hash of L2 batch
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param priorityOperationsHash Hash of all priority operations from this batch
/// @param l2LogsTreeRoot Root hash of tree that contains L2 -> L1 messages from this batch
/// @param timestamp Rollup batch timestamp, have the same format as Ethereum batch constant
/// @param commitment Verified input for the ZKsync circuit
// solhint-disable-next-line gas-struct-packing
struct StoredBatchInfo {
uint64 batchNumber;
bytes32 batchHash;
uint64 indexRepeatedStorageChanges;
uint256 numberOfLayer1Txs;
bytes32 priorityOperationsHash;
bytes32 l2LogsTreeRoot;
uint256 timestamp;
bytes32 commitment;
}
/// @notice Data needed to commit new batch
/// @param batchNumber Number of the committed batch
/// @param timestamp Unix timestamp denoting the start of the batch execution
/// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more
/// @param newStateRoot The state root of the full state tree
/// @param numberOfLayer1Txs Number of priority operations to be processed
/// @param priorityOperationsHash Hash of all priority operations from this batch
/// @param bootloaderHeapInitialContentsHash Hash of the initial contents of the bootloader heap. In practice it serves as the commitment to the transactions in the batch.
/// @param eventsQueueStateHash Hash of the events queue state. In practice it serves as the commitment to the events in the batch.
/// @param systemLogs concatenation of all L2 -> L1 system logs in the batch
/// @param pubdataCommitments Packed pubdata commitments/data.
/// @dev pubdataCommitments format: This will always start with a 1 byte pubdataSource flag. Current allowed values are 0 (calldata) or 1 (blobs)
/// kzg: list of: opening point (16 bytes) || claimed value (32 bytes) || commitment (48 bytes) || proof (48 bytes) = 144 bytes
/// calldata: pubdataCommitments.length - 1 - 32 bytes of pubdata
/// and 32 bytes appended to serve as the blob commitment part for the aux output part of the batch commitment
/// @dev For 2 blobs we will be sending 288 bytes of calldata instead of the full amount for pubdata.
/// @dev When using calldata, we only need to send one blob commitment since the max number of bytes in calldata fits in a single blob and we can pull the
/// linear hash from the system logs
struct CommitBatchInfo {
uint64 batchNumber;
uint64 timestamp;
uint64 indexRepeatedStorageChanges;
bytes32 newStateRoot;
uint256 numberOfLayer1Txs;
bytes32 priorityOperationsHash;
bytes32 bootloaderHeapInitialContentsHash;
bytes32 eventsQueueStateHash;
bytes systemLogs;
bytes pubdataCommitments;
}
/// @notice Recursive proof input data (individual commitments are constructed onchain)
struct ProofInput {
uint256[] recursiveAggregationInput;
uint256[] serializedProof;
}
/// @notice Function called by the operator to commit new batches. It is responsible for:
/// - Verifying the correctness of their timestamps.
/// - Processing their L2->L1 logs.
/// - Storing batch commitments.
/// @param _lastCommittedBatchData Stored data of the last committed batch.
/// @param _newBatchesData Data of the new batches to be committed.
function commitBatches(
StoredBatchInfo calldata _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) external;
/// @notice same as `commitBatches` but with the chainId so ValidatorTimelock can sort the inputs.
function commitBatchesSharedBridge(
uint256 _chainId,
StoredBatchInfo calldata _lastCommittedBatchData,
CommitBatchInfo[] calldata _newBatchesData
) external;
/// @notice Batches commitment verification.
/// @dev Only verifies batch commitments without any other processing.
/// @param _prevBatch Stored data of the last committed batch.
/// @param _committedBatches Stored data of the committed batches.
/// @param _proof The zero knowledge proof.
function proveBatches(
StoredBatchInfo calldata _prevBatch,
StoredBatchInfo[] calldata _committedBatches,
ProofInput calldata _proof
) external;
/// @notice same as `proveBatches` but with the chainId so ValidatorTimelock can sort the inputs.
function proveBatchesSharedBridge(
uint256 _chainId,
StoredBatchInfo calldata _prevBatch,
StoredBatchInfo[] calldata _committedBatches,
ProofInput calldata _proof
) external;
/// @notice The function called by the operator to finalize (execute) batches. It is responsible for:
/// - Processing all pending operations (commpleting priority requests).
/// - Finalizing this batch (i.e. allowing to withdraw funds from the system)
/// @param _batchesData Data of the batches to be executed.
function executeBatches(StoredBatchInfo[] calldata _batchesData) external;
/// @notice same as `executeBatches` but with the chainId so ValidatorTimelock can sort the inputs.
function executeBatchesSharedBridge(uint256 _chainId, StoredBatchInfo[] calldata _batchesData) external;
/// @notice Reverts unexecuted batches
/// @param _newLastBatch batch number after which batches should be reverted
/// NOTE: Doesn't delete the stored data about batches, but only decreases
/// counters that are responsible for the number of batches
function revertBatches(uint256 _newLastBatch) external;
/// @notice same as `revertBatches` but with the chainId so ValidatorTimelock can sort the inputs.
function revertBatchesSharedBridge(uint256 _chainId, uint256 _newLastBatch) external;
/// @notice Event emitted when a batch is committed
/// @param batchNumber Number of the batch committed
/// @param batchHash Hash of the L2 batch
/// @param commitment Calculated input for the ZKsync circuit
/// @dev It has the name "BlockCommit" and not "BatchCommit" due to backward compatibility considerations
event BlockCommit(uint256 indexed batchNumber, bytes32 indexed batchHash, bytes32 indexed commitment);
/// @notice Event emitted when batches are verified
/// @param previousLastVerifiedBatch Batch number of the previous last verified batch
/// @param currentLastVerifiedBatch Batch number of the current last verified batch
/// @dev It has the name "BlocksVerification" and not "BatchesVerification" due to backward compatibility considerations
event BlocksVerification(uint256 indexed previousLastVerifiedBatch, uint256 indexed currentLastVerifiedBatch);
/// @notice Event emitted when a batch is executed
/// @param batchNumber Number of the batch executed
/// @param batchHash Hash of the L2 batch
/// @param commitment Verified input for the ZKsync circuit
/// @dev It has the name "BlockExecution" and not "BatchExecution" due to backward compatibility considerations
event BlockExecution(uint256 indexed batchNumber, bytes32 indexed batchHash, bytes32 indexed commitment);
/// @notice Event emitted when batches are reverted
/// @param totalBatchesCommitted Total number of committed batches after the revert
/// @param totalBatchesVerified Total number of verified batches after the revert
/// @param totalBatchesExecuted Total number of executed batches
/// @dev It has the name "BlocksRevert" and not "BatchesRevert" due to backward compatibility considerations
event BlocksRevert(uint256 totalBatchesCommitted, uint256 totalBatchesVerified, uint256 totalBatchesExecuted);
}
// 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,
uint256[] calldata _recursiveAggregationInput
) 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 pure 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;
/// @title The interface of the ZKsync contract, responsible for the main ZKsync logic.
/// @author Matter Labs
/// @custom:security-contact [email protected]
interface IZkSyncHyperchainBase {
/// @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 {Diamond} from "./libraries/Diamond.sol";
import {L2CanonicalTransaction} from "../common/Messaging.sol";
import {FeeParams} from "./chain-deps/ZkSyncHyperchainStorage.sol";
/// @notice Struct that holds all data needed for initializing STM 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
struct StateTransitionManagerInitializeData {
address owner;
address validatorTimelock;
ChainCreationParams chainCreationParams;
uint256 protocolVersion;
}
/// @notice The struct that contains the fields that define how a new chain should be created
/// within this STM.
/// @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;
}
interface IStateTransitionManager {
/// @dev Emitted when a new Hyperchain is added
event NewHyperchain(uint256 indexed _chainId, address indexed _hyperchainContract);
/// @dev emitted when an chain registers and a SetChainIdUpgrade happens
event SetChainIdUpgrade(
address indexed _hyperchain,
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 chain creation parameters changed
event NewChainCreationParams(
address genesisUpgrade,
bytes32 genesisBatchHash,
uint64 genesisIndexRepeatedStorageChanges,
bytes32 genesisBatchCommitment,
bytes32 newInitialCutHash
);
/// @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);
function BRIDGE_HUB() external view returns (address);
function setPendingAdmin(address _newPendingAdmin) external;
function acceptAdmin() external;
function getAllHyperchains() external view returns (address[] memory);
function getAllHyperchainChainIDs() external view returns (uint256[] memory);
function getHyperchain(uint256 _chainId) external view returns (address);
function storedBatchZero() external view returns (bytes32);
function initialCutHash() external view returns (bytes32);
function genesisUpgrade() 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 initialize(StateTransitionManagerInitializeData 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,
address _baseToken,
address _sharedBridge,
address _admin,
bytes calldata _diamondCut
) external;
function registerAlreadyDeployedHyperchain(uint256 _chainId, address _hyperchain) external;
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);
}
// 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
// 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;
}
}