Skip to content
Lattice

GovernedDiamondCutLib

Git Source

Title: GovernedDiamondCut Library

Author: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80)

Library that gates EIP-2535 diamond cuts behind EmergencyStop + a single-holder role, then delegates the actual cut to DiamondLib.diamondCut. Introduces no new cut logic.

Three-layer pattern: this library holds all logic and the namespaced storage. The stateless GovernedDiamondCut facet delegates its single external call here.

Functions

governedDiamondCutStorage

Returns a storage reference to the GovernedDiamondCutStorage struct.

function governedDiamondCutStorage() internal pure returns (GovernedDiamondCutStorage storage $);

Returns

NameTypeDescription
$GovernedDiamondCutStorage

__GovernedDiamondCut_init

Initializes the GovernedDiamondCut module.

Grants UPGRADE_EXECUTOR_ROLE to address(this) AND pins that role to administer ITSELF, so the role is held solely by address(this) and cannot be granted by anyone else. The self-administration step is load-bearing: without it the role's admin defaults to DEFAULT_ADMIN_ROLE (0x00), which would let any DEFAULT_ADMIN_ROLE holder (e.g. a live admin EOA) grantRole(UPGRADE_EXECUTOR_ROLE, attacker) and then diamondCut directly, bypassing the entire Governor + TimelockController + vote + delay path. With the role self-administered, only an existing holder can grant it — and the only holder is address(this), which the recipe reaches only through a timelock-relayed passed proposal. A co-cut facet that relays outside calls back into the diamond (e.g. AccessManager execute) reaches it too; see the UPGRADE_EXECUTOR_ROLE caveat above. _setRoleAdmin is the UNGATED setter (the gated setRoleAdmin would require the initializer to itself hold the current admin role, which is not guaranteed during bootstrap). Must be called inside the preInitializer/postInitializer window; AccessControl must already be initialized (the role writes target its storage). Does NOT register an ERC-165 interface: 0x1f931c1c is already registered by DiamondLib.registerInterface() (see the ERC165_MAP_ICUT_SLOT note above).

function __GovernedDiamondCut_init() internal;

diamondCut

Guarded diamond cut: reverts if emergency-stopped, then requires the caller to hold UPGRADE_EXECUTOR_ROLE, then delegates to DiamondLib.diamondCut. Introduces no new cut logic — all selector-collision, immutable-function, bytecode-existence, and init-delegatecall handling is diamond-lib's.

function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) internal;

Parameters

NameTypeDescription
_diamondCutFacetCut[]The facet addresses, cut actions, and function selectors.
_initaddressThe address delegatecalled after the cut (address(0) to skip).
_calldatabytesThe calldata passed to _init.

emergencyRemoveCut

Zero-delay, REMOVAL-ONLY escape hatch: lets a guardian instantly unbind a compromised or buggy facet's selectors WITHOUT a governance round — and, by design, even while the normal cut path is halted by EmergencyStop. Deliberately constrained so a rogue guardian can only AMPUTATE code (every entry must be Remove), never add or replace it (that still requires diamondCut under full governance), can never remove diamondCut itself or a stop-recovery selector (even when unfrozen), and can never remove a frozen selector. It CAN remove any other unfrozen selector, including the Governor/Timelock path that reaches diamondCut, so the guardian is trusted for governance liveness until that path is frozen (see the EMERGENCY_GUARDIAN_ROLE note above).

Guard ordering and rationale:

  1. Authority FIRST: only an EMERGENCY_GUARDIAN_ROLE holder may proceed.
  2. NO EmergencyStopLib.checkNotStopped() — INTENTIONAL. This is the panic button; it MUST remain usable precisely when upgrades are stopped (the same guardian who tripped the stop uses it to surgically remove the offending facet). The governed diamondCut keeps the stop gate; only this removal-only path bypasses it.
  3. Removal-only: every FacetCut action must be Remove; an Add/Replace reverts IEmergencyCut.EmergencyCutMustBeRemoveOnly with the offending action. diamond-lib additionally enforces facetAddress == address(0) for Remove.
  4. Entrypoint + frozen protection: diamondCut and the stop-recovery selectors (EmergencyStopLib.isRecoverySelector) revert IEmergencyCut.EmergencyCutEntrypointProtected even with an empty frozen set, and a frozen selector reverts IFrozenSelectors.FrozenSelectorProtected.
  5. NO init delegatecall: applied as DiamondLib.diamondCut(cuts, address(0), ""). A pure removal has nothing to initialize, and forbidding init denies a rogue guardian any arbitrary-delegatecall vector. The removal is recorded in the SAME append-only registry as governed cuts (so it appears in cut history) and additionally emits IEmergencyCut.EmergencyCutExecuted so it is auditable as an emergency action.
function emergencyRemoveCut(FacetCut[] calldata _cuts) internal;

Parameters

NameTypeDescription
_cutsFacetCut[]The facet cuts to apply; every entry MUST be a Remove (facetAddress == address(0)).

freezeSelectors

Permanently freezes _selectors: each becomes ineligible to be the target of a Replace/Remove in any future governed cut. Gated behind UPGRADE_EXECUTOR_ROLE — the same single-holder role that gates diamondCut — so freezing takes a timelock-relayed governance proposal (or whatever else can reach that role; see the caveat on UPGRADE_EXECUTOR_ROLE). Append-only: already-frozen selectors are idempotent no-ops, and there is no unfreeze.

function freezeSelectors(bytes4[] calldata _selectors) internal;

Parameters

NameTypeDescription
_selectorsbytes4[]The function selectors to freeze.

_enforceNotFrozen

Reverts IFrozenSelectors.FrozenSelectorProtected if any Replace/Remove action in _diamondCut targets a frozen selector. Add actions are never inspected. This is the shared core of both the guarded diamondCut (reverting form) and previewCut (the reverting form is reused via a try/catch-free direct read in previewCut).

function _enforceNotFrozen(GovernedDiamondCutStorage storage $, FacetCut[] calldata _diamondCut) private view;

Parameters

NameTypeDescription
$GovernedDiamondCutStorage
_diamondCutFacetCut[]

cutCount

Returns the number of governed cuts applied so far (also the latest registry version).

function cutCount() internal view returns (uint256);

Returns

NameTypeDescription
<none>uint256

getCutRecord

Returns the immutable audit record for a given registry version.

Versions are 1-indexed; an unwritten version (0, or any value > cutCount) returns a zero-valued IUpgradeRegistry.CutRecord.

function getCutRecord(uint256 _version) internal view returns (IUpgradeRegistry.CutRecord memory);

Parameters

NameTypeDescription
_versionuint256The registry version to look up.

Returns

NameTypeDescription
<none>IUpgradeRegistry.CutRecord

isSelectorFrozen

Returns whether _selector is in the frozen set (protected from Replace/Remove).

function isSelectorFrozen(bytes4 _selector) internal view returns (bool);

Parameters

NameTypeDescription
_selectorbytes4The function selector to query.

Returns

NameTypeDescription
<none>bool

frozenSelectors

Returns the full set of frozen selectors.

function frozenSelectors() internal view returns (bytes4[] memory);

Returns

NameTypeDescription
<none>bytes4[]

previewCut

Pre-flight simulation of the frozen-selector guard against the current set, with no state change. Mirrors exactly the check the guarded diamondCut performs before applying: only Replace/Remove actions are inspected. Pure read of the frozen set — it does NOT re-implement diamond-lib's collision/immutable/bytecode checks (those fire at execution).

function previewCut(FacetCut[] calldata _cuts) internal view returns (bool ok, bytes4 offendingSelector);

Parameters

NameTypeDescription
_cutsFacetCut[]The candidate facet cuts to simulate.

Returns

NameTypeDescription
okbooltrue if no Replace/Remove in _cuts targets a frozen selector.
offendingSelectorbytes4The first frozen selector a Replace/Remove would touch (zero if ok).

verifyInterfaceRegistered

Verifies whether _interfaceId is currently advertised in the diamond's ERC-165 map.

Thin wrapper over the shared ERC-165 support read so an upgrade workflow can confirm an expected interface is still/now advertised after a cut. Equivalent to supportsInterface.

function verifyInterfaceRegistered(bytes4 _interfaceId) internal view returns (bool);

Parameters

NameTypeDescription
_interfaceIdbytes4The ERC-165 interface identifier to check.

Returns

NameTypeDescription
<none>bool