Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
96116ea
fix: foundry fixes (#1981)
kelemeno Jan 21, 2026
fea9b2a
get upgradeTimstamp from chainAdmin
kelemeno Jan 21, 2026
0f0ddcf
add upgrader role to ValidatorTimelock
kelemeno Jan 21, 2026
1b0c83f
fixes
tomg10 Jan 29, 2026
6bb00a4
revert invalid change
tomg10 Jan 30, 2026
340cfac
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 5, 2026
379df1d
update interface
kelemeno Feb 6, 2026
7be1633
selectors
kelemeno Feb 6, 2026
8b9045b
fix selectors mismatch
kelemeno Feb 6, 2026
8dfb89b
selectors
kelemeno Feb 6, 2026
0262c7a
test(l1-contracts): upgradeChainFromVersion propagates to diamond proxy
kelemeno Feb 6, 2026
8d5f6f7
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 11, 2026
a50fa6d
small lint fixes
kelemeno Feb 11, 2026
1a9e432
zkstack out
kelemeno Feb 11, 2026
d793c6c
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 12, 2026
76ef038
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 13, 2026
1b7d8b5
selectors
kelemeno Feb 13, 2026
3f91df8
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 13, 2026
1652902
deduplicate empty upgrade
kelemeno Feb 13, 2026
94ae8f2
cleanup
kelemeno Feb 13, 2026
274421a
fix test
kelemeno Feb 13, 2026
b042901
Merge branch 'draft-v31' of ssh://github.com/matter-labs/era-contract…
kelemeno Feb 24, 2026
ed557c8
Merge remote-tracking branch 'origin/draft-v31' into kl/automatic-upg…
Raid5594 Mar 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions l1-contracts/contracts/governance/IChainAdmin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ interface IChainAdmin {
/// @notice The EVM emulator has been enabled
event EnableEvmEmulator();

/// @notice Returns the upgrade timestamp for a specific protocol version.
/// @param _protocolVersion The protocol version to query.
/// @return The timestamp at which the upgrade is expected.
function protocolVersionToUpgradeTimestamp(uint256 _protocolVersion) external view returns (uint256);

/// @notice Returns the list of active restrictions.
function getRestrictions() external view returns (address[] memory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ import {IL1Bridgehub} from "../core/bridgehub/IL1Bridgehub.sol";
import {IChainAssetHandlerBase} from "../core/chain-asset-handler/IChainAssetHandler.sol";

import {ReentrancyGuard} from "../common/ReentrancyGuard.sol";
import {L2CanonicalTransaction, TxStatus} from "../common/Messaging.sol";
import {ProposedUpgrade} from "../upgrades/BaseZkSyncUpgrade.sol";
import {TxStatus} from "../common/Messaging.sol";
import {ProposedUpgrade, ProposedUpgradeLib} from "./libraries/ProposedUpgradeLib.sol";
import {IDefaultUpgrade} from "../upgrades/IDefaultUpgrade.sol";
import {VerifierParams} from "./chain-interfaces/IVerifier.sol";

/// @title Chain Type Manager Base contract
/// @author Matter Labs
Expand Down Expand Up @@ -398,39 +397,7 @@ abstract contract ChainTypeManagerBase is IChainTypeManager, ReentrancyGuard, Ow
}

// Construct minimal ProposedUpgrade for patch (VK-only) upgrade
ProposedUpgrade memory proposedUpgrade = ProposedUpgrade({
l2ProtocolUpgradeTx: L2CanonicalTransaction({
txType: 0,
from: uint256(0),
to: uint256(0),
gasLimit: 0,
gasPerPubdataByteLimit: 0,
maxFeePerGas: 0,
maxPriorityFeePerGas: 0,
paymaster: 0,
nonce: 0,
value: 0,
reserved: [uint256(0), 0, 0, 0],
data: "",
signature: "",
factoryDeps: new uint256[](0),
paymasterInput: "",
reservedDynamic: ""
}),
bootloaderHash: bytes32(0),
defaultAccountHash: bytes32(0),
evmEmulatorHash: bytes32(0),
verifier: address(0),
verifierParams: VerifierParams({
recursionNodeLevelVkHash: bytes32(0),
recursionLeafLevelVkHash: bytes32(0),
recursionCircuitsSetVksHash: bytes32(0)
}),
l1ContractsUpgradeCalldata: "",
postUpgradeCalldata: "",
upgradeTimestamp: 0,
newProtocolVersion: _newProtocolVersion
});
ProposedUpgrade memory proposedUpgrade = ProposedUpgradeLib.emptyProposedUpgrade(_newProtocolVersion);

bytes memory upgradeCalldata = abi.encodeCall(IDefaultUpgrade.upgrade, (proposedUpgrade));

Expand Down Expand Up @@ -550,7 +517,8 @@ abstract contract ChainTypeManagerBase is IChainTypeManager, ReentrancyGuard, Ow
uint256 _oldProtocolVersion,
Diamond.DiamondCutData calldata _diamondCut
) external onlyOwner {
IZKChain(getZKChain(_chainId)).upgradeChainFromVersion(_oldProtocolVersion, _diamondCut);
address chainAddress = getZKChain(_chainId);
IZKChain(chainAddress).upgradeChainFromVersion(chainAddress, _oldProtocolVersion, _diamondCut);
}

/// @dev executes upgrade on chain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {RollupDAManager} from "../../data-availability/RollupDAManager.sol";
import {PriorityTree} from "../../libraries/PriorityTree.sol";
import {L2_DEPLOYER_SYSTEM_CONTRACT_ADDR} from "../../../common/l2-helpers/L2ContractAddresses.sol";
import {AllowedBytecodeTypes, IL2ContractDeployer} from "../../../common/interfaces/IL2ContractDeployer.sol";

import {IChainAdmin} from "../../../governance/IChainAdmin.sol";
// While formally the following import is not used, it is needed to inherit documentation from it
import {IZKChainBase} from "../../chain-interfaces/IZKChainBase.sol";

Expand Down Expand Up @@ -449,9 +449,10 @@ contract AdminFacet is ZKChainBase, IAdmin {

/// @inheritdoc IAdmin
function upgradeChainFromVersion(
address, // _chainAddress (unused in this specific implementation)
uint256 _oldProtocolVersion,
Diamond.DiamondCutData calldata _diamondCut
) external onlyAdminOrChainTypeManager {
) external onlyAdminOrChainTypeManagerOrValidator {
bytes32 cutHashInput = keccak256(abi.encode(_diamondCut));
bytes32 upgradeCutHash = IChainTypeManager(s.chainTypeManager).upgradeCutHash(_oldProtocolVersion);
if (cutHashInput != upgradeCutHash) {
Expand All @@ -461,6 +462,14 @@ contract AdminFacet is ZKChainBase, IAdmin {
if (s.protocolVersion != _oldProtocolVersion) {
revert ProtocolIdMismatch(s.protocolVersion, _oldProtocolVersion);
}

// Check that the auto upgrade timestamp has passed if the sender is not admin or chainTypeManager
if (msg.sender != s.admin && msg.sender != s.chainTypeManager) {
uint256 timestamp = IChainAdmin(s.admin).protocolVersionToUpgradeTimestamp(_oldProtocolVersion);
if (block.timestamp < timestamp) {
revert Unauthorized(msg.sender);
}
}
_executeDiamondCut(_diamondCut);
if (s.protocolVersion <= _oldProtocolVersion) {
revert ProtocolIdNotGreater();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,26 @@ contract ZKChainBase is ReentrancyGuard {

/// @notice Checks that the message sender is an active admin
modifier onlyAdmin() {
_onlyAdmin();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did we introduce so many changes in this file?

_;
}

function _onlyAdmin() internal view {
if (msg.sender != s.admin) {
revert Unauthorized(msg.sender);
}
_;
}

/// @notice Checks if validator is active
modifier onlyValidator() {
_onlyValidator();
_;
}

function _onlyValidator() internal view {
if (!s.validators[msg.sender]) {
revert Unauthorized(msg.sender);
}
_;
}

/// @notice Ensures Priority Mode is not active.
Expand Down Expand Up @@ -90,69 +98,121 @@ contract ZKChainBase is ReentrancyGuard {
}

modifier onlyChainTypeManager() {
_onlyChainTypeManager();
_;
}

function _onlyChainTypeManager() internal view {
if (msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyBridgehub() {
_onlyBridgehub();
_;
}

function _onlyBridgehub() internal view {
if (msg.sender != s.bridgehub) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyBridgehubOrInteropCenter() {
_onlyBridgehubOrInteropCenter();
_;
}

function _onlyBridgehubOrInteropCenter() internal view {
if ((msg.sender != s.bridgehub) && (msg.sender != L2_INTEROP_CENTER_ADDR)) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyGatewayAssetTracker() {
_onlyGatewayAssetTracker();
_;
}

function _onlyGatewayAssetTracker() internal view {
if (msg.sender != GW_ASSET_TRACKER_ADDR) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyChainAssetHandler() {
_onlyChainAssetHandler();
_;
}

function _onlyChainAssetHandler() internal view {
if (msg.sender != IL1Bridgehub(s.bridgehub).chainAssetHandler()) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyAdminOrChainTypeManager() {
_onlyAdminOrChainTypeManager();
_;
}

function _onlyAdminOrChainTypeManager() internal view {
if (msg.sender != s.admin && msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
}

modifier onlyAdminOrChainTypeManagerOrValidator() {
_onlyAdminOrChainTypeManagerOrValidator();
_;
}

function _onlyAdminOrChainTypeManagerOrValidator() internal view {
if (msg.sender != s.admin && msg.sender != s.chainTypeManager && !s.validators[msg.sender]) {
revert Unauthorized(msg.sender);
}
}

modifier onlyValidatorOrChainTypeManager() {
_onlyValidatorOrChainTypeManager();
_;
}

function _onlyValidatorOrChainTypeManager() internal view {
if (!s.validators[msg.sender] && msg.sender != s.chainTypeManager) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlySettlementLayer() {
_onlySettlementLayer();
_;
}

function _onlySettlementLayer() internal view {
if (s.settlementLayer != address(0)) {
revert NotSettlementLayer();
}
_;
}

modifier onlySelf() {
_onlySelf();
_;
}

function _onlySelf() internal view {
if (msg.sender != address(this)) {
revert Unauthorized(msg.sender);
}
_;
}

modifier onlyServiceTransaction() {
_onlyServiceTransaction();
_;
}

function _onlyServiceTransaction() internal view {
IBridgehubBase bridgehub = IBridgehubBase(s.bridgehub);
if (
/// Purposes.
Expand All @@ -169,7 +229,6 @@ contract ZKChainBase is ReentrancyGuard {
) {
revert Unauthorized(msg.sender);
}
_;
}

/// @notice Returns whether the priority queue is still active, i.e.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,14 @@ interface IAdmin is IZKChainBase {
function activatePriorityMode() external;

/// @notice Perform the upgrade from the current protocol version with the corresponding upgrade data
/// @param _chainAddress The address of the chain being upgraded
/// @param _protocolVersion The current protocol version from which upgrade is executed
/// @param _cutData The diamond cut parameters that is executed in the upgrade
function upgradeChainFromVersion(uint256 _protocolVersion, Diamond.DiamondCutData calldata _cutData) external;
function upgradeChainFromVersion(
address _chainAddress,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that you have not yet worked on this change on the server side, but this is a breaking change for zkstack cli etc

uint256 _protocolVersion,
Diamond.DiamondCutData calldata _cutData
) external;

/// @notice Executes a proposed governor upgrade
/// @dev Only the ChainTypeManager contract can execute the upgrade
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.28;

import {L2CanonicalTransaction} from "../../common/Messaging.sol";
import {VerifierParams} from "../chain-interfaces/IVerifier.sol";

/// @notice The struct that represents the upgrade proposal.
/// @param l2ProtocolUpgradeTx The system upgrade transaction.
/// @param bootloaderHash The hash of the new bootloader bytecode. If zero, it will not be updated.
/// @param defaultAccountHash The hash of the new default account bytecode. If zero, it will not be updated.
/// @param evmEmulatorHash The hash of the new EVM emulator bytecode. If zero, it will not be updated.
/// @param verifier Deprecated. Verifier is fetched from CTM based on protocol version.
/// @param verifierParams Deprecated. Verifier params are kept for backward compatibility.
/// @param l1ContractsUpgradeCalldata Custom calldata for L1 contracts upgrade, it may be interpreted differently
/// in each upgrade. Usually empty.
/// @param postUpgradeCalldata Custom calldata for post upgrade hook, it may be interpreted differently in each
/// upgrade. Usually empty.
/// @param upgradeTimestamp The timestamp after which the upgrade can be executed.
/// @param newProtocolVersion The new version number for the protocol after this upgrade. Should be greater than
/// the previous protocol version.
struct ProposedUpgrade {
L2CanonicalTransaction l2ProtocolUpgradeTx;
bytes32 bootloaderHash;
bytes32 defaultAccountHash;
bytes32 evmEmulatorHash;
address verifier;
VerifierParams verifierParams;
bytes l1ContractsUpgradeCalldata;
bytes postUpgradeCalldata;
uint256 upgradeTimestamp;
uint256 newProtocolVersion;
}

/// @notice Helpers for constructing zero-initialised upgrade structs.
/// @dev Shared between runtime contracts (ChainTypeManagerBase) and deploy scripts (CTMUpgradeBase)
/// to avoid manual zero-struct assembly that can desync when fields change.
library ProposedUpgradeLib {
function emptyL2CanonicalTransaction() internal pure returns (L2CanonicalTransaction memory) {
return
L2CanonicalTransaction({
txType: 0,
from: 0,
to: 0,
gasLimit: 0,
gasPerPubdataByteLimit: 0,
maxFeePerGas: 0,
maxPriorityFeePerGas: 0,
paymaster: 0,
nonce: 0,
value: 0,
reserved: [uint256(0), 0, 0, 0],
data: "",
signature: "",
factoryDeps: new uint256[](0),
paymasterInput: "",
reservedDynamic: ""
});
}

function emptyVerifierParams() internal pure returns (VerifierParams memory) {
return
VerifierParams({
recursionNodeLevelVkHash: bytes32(0),
recursionLeafLevelVkHash: bytes32(0),
recursionCircuitsSetVksHash: bytes32(0)
});
}

function emptyProposedUpgrade(uint256 _newProtocolVersion) internal pure returns (ProposedUpgrade memory) {
return
ProposedUpgrade({
l2ProtocolUpgradeTx: emptyL2CanonicalTransaction(),
bootloaderHash: bytes32(0),
defaultAccountHash: bytes32(0),
evmEmulatorHash: bytes32(0),
verifier: address(0),
verifierParams: emptyVerifierParams(),
l1ContractsUpgradeCalldata: "",
postUpgradeCalldata: "",
upgradeTimestamp: 0,
newProtocolVersion: _newProtocolVersion
});
}
}
Loading
Loading