GovernedSafeDiamondCutLib
Title: GovernedSafeDiamondCut Library
Author: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80)
Library that gates EIP-2535 diamond cuts behind EmergencyStop + a pinned Gnosis Safe
multisig AND a self-contained timelock: cuts must be scheduled, mature past minDelay, and
only then be executed. No external Governor/TimelockController is required. 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
GovernedSafeDiamondCut facet delegates its external calls here. AUTHORITY MODEL: a Safe
collects M-of-N owner signatures off-chain, verifies the threshold on-chain in
execTransaction, then calls the diamond with operation = Call, so the diamond sees
msg.sender == theSafe. This module does NOT re-verify signatures — it only trusts
msg.sender == $._safe. The Safe MUST call with operation = Call (NEVER DelegateCall).
Unlike SafeDiamondCutLib this module does NOT expose a synchronous cut at 0x1f931c1c; every
cut travels schedule -> delay -> execute, so its scheduling surface is a NEW ERC-165 interface
(0xacb1aeb6) registered at init.
Constants
DONE_TIMESTAMP
_scheduledAt marker for an executed operation (OpenZeppelin's _DONE_TIMESTAMP). It keeps an
executed id distinct from a never-scheduled or cancelled one (0) and blocks its replay. As in
OpenZeppelin, an eta that lands on a marker is misread: with a zero delay, scheduling at
timestamp 1 reads as Done (cannot execute or cancel; use another salt) and at timestamp 0 as
Unset. Neither timestamp occurs on a live chain.
uint256 internal constant DONE_TIMESTAMP = 1;Functions
governedSafeDiamondCutStorage
Returns a storage reference to the GovernedSafeDiamondCutStorage struct.
function governedSafeDiamondCutStorage() internal pure returns (GovernedSafeDiamondCutStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | GovernedSafeDiamondCutStorage |
registerInterface
Registers support for the IGovernedSafeDiamondCut interface via ERC-165.
Writes true to the precomputed ERC-165 map slot (see ERC165_MAP_IGOVERNEDSAFEDIAMONDCUT_SLOT).
function registerInterface() internal;__GovernedSafeDiamondCut_init
Initializes the GovernedSafeDiamondCut module by pinning the Safe authority and timelock.
Validates _safe is non-zero and a real Safe whose getThreshold() meets _minThreshold
(which must itself be > 0). _minDelay MAY be 0, but a zero delay makes the timelock a
no-op (a scheduled cut is immediately executable) — set a meaningful delay in production so
the multisig's intent is publicly observable before it takes effect. There is no floor or
ceiling: a delay so large that block.timestamp + delay overflows makes every scheduleCut
and setMinDelay revert, permanently. Must be called inside the
preInitializer/postInitializer window. Registers the IGovernedSafeDiamondCut ERC-165 id.
function __GovernedSafeDiamondCut_init(address _safe, uint256 _minThreshold, uint256 _minDelay) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _safe | address | The Safe multisig to pin as the cut authority. |
| _minThreshold | uint256 | The minimum signature threshold the pinned Safe must enforce. |
| _minDelay | uint256 | The minimum timelock delay (seconds) between schedule and execute. |
_checkSafe
Reverts ISafeAuthority.SafeDiamondCutUnauthorized unless the caller is the pinned Safe.
function _checkSafe(GovernedSafeDiamondCutStorage storage $) private view;Parameters
| Name | Type | Description |
|---|---|---|
| $ | GovernedSafeDiamondCutStorage |
_validateSafe
Validates a candidate Safe authority: non-zero, real Safe, threshold >= minimum (> 0).
function _validateSafe(address _safe, uint256 _minThreshold) private view;Parameters
| Name | Type | Description |
|---|---|---|
| _safe | address | |
| _minThreshold | uint256 |
setSafe
Rotates the pinned Safe authority to _newSafe. Self-administered: callable ONLY by the
current pinned Safe. _newSafe is validated like the init Safe (threshold >= 1). Emits
ISafeAuthority.SafeRotated.
function setSafe(address _newSafe) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _newSafe | address | The new Safe to pin as the cut authority. |
setMinDelay
Requests a new minimum timelock delay (seconds). Self-administered: callable ONLY by the
pinned Safe. The change is NOT immediate: it applies to operations scheduled from
effectAt = block.timestamp + max(current, _newDelay), so lowering the delay waits out the
delay it replaces and the Safe cannot zero the delay and push a cut through in one
transaction. A new request replaces any pending one (re-requesting the current delay
withdraws it). Emits IGovernedSafeDiamondCut.MinDelayChangeScheduled.
Pending-delay pattern after OpenZeppelin's AccessManager. Not gated on msg.sender == address(this) like OpenZeppelin's TimelockController updateDelay: in a composed diamond
address(this) can be reachable through other facets. Reverts on overflow of effectAt.
function setMinDelay(uint256 _newDelay) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _newDelay | uint256 | The new minimum delay (seconds); may be 0 (see init NatSpec on the risk). |
_minDelayInForce
The delay in force now: the requested delay once its effectAt has passed, else the last
committed one.
function _minDelayInForce(GovernedSafeDiamondCutStorage storage $) private view returns (uint256);Parameters
| Name | Type | Description |
|---|---|---|
| $ | GovernedSafeDiamondCutStorage |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
safe
Returns the currently pinned Safe authority.
function safe() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
minDelay
Returns the minimum timelock delay (seconds) in force now. A change requested through
setMinDelay is reflected only from its effectAt.
function minDelay() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
_operationId
Computes the operation id binding the full cut payload plus a disambiguating salt.
function _operationId(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata, bytes32 _salt)
private
pure
returns (bytes32);Parameters
| Name | Type | Description |
|---|---|---|
| _diamondCut | FacetCut[] | |
| _init | address | |
| _calldata | bytes | |
| _salt | bytes32 |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bytes32 |
_state
Classifies a stored _scheduledAt value: 0 is Unset (never scheduled or cancelled),
DONE_TIMESTAMP is Done, and any other value is Waiting until its eta, then Ready.
function _state(uint256 _eta) private view returns (ITimelockController.OperationState);Parameters
| Name | Type | Description |
|---|---|---|
| _eta | uint256 |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | ITimelockController.OperationState |
scheduleCut
Schedules a cut for later execution. Reverts if emergency-stopped, then requires the
caller to be the pinned Safe, then stores eta = block.timestamp + minDelay under the
operation id (checked: a delay that would overflow reverts rather than wrap eta into the
past). Reverts IGovernedSafeDiamondCut.CutAlreadyScheduled if the id is already
scheduled or was executed; re-running an executed cut takes a new salt. Does NOT validate
frozen selectors here — that check fires at execution time (against the then-current
frozen set), matching the synchronous path's ordering.
function scheduleCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata, bytes32 _salt)
internal
returns (bytes32 id);Parameters
| Name | Type | Description |
|---|---|---|
| _diamondCut | FacetCut[] | The facet addresses, cut actions, and function selectors. |
| _init | address | The address delegatecalled after the cut (address(0) to skip). |
| _calldata | bytes | The calldata passed to _init. |
| _salt | bytes32 | A caller-chosen salt disambiguating otherwise-identical payloads. |
Returns
| Name | Type | Description |
|---|---|---|
| id | bytes32 | The operation id. |
executeCut
Executes a previously-scheduled, matured cut. Reverts if emergency-stopped, requires the
caller to be the pinned Safe, re-derives the id and requires it scheduled and matured
(an executed id reverts IGovernedSafeDiamondCut.CutNotScheduled), marks it done, enforces
the frozen-selector guard, applies the cut, and records it in the append-only registry.
Emits IGovernedSafeDiamondCut.CutExecuted.
function executeCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata, bytes32 _salt)
internal;Parameters
| Name | Type | Description |
|---|---|---|
| _diamondCut | FacetCut[] | The facet addresses, cut actions, and function selectors (must match schedule). |
| _init | address | The address delegatecalled after the cut (must match schedule). |
| _calldata | bytes | The calldata passed to _init (must match schedule). |
| _salt | bytes32 | The salt used at schedule time. |
cancelCut
Cancels a scheduled, not-yet-executed operation (pending or ready). Requires the caller to
be the pinned Safe. Reverts IGovernedSafeDiamondCut.CutNotScheduled if the id is not
currently scheduled, including an executed id. Emits IGovernedSafeDiamondCut.CutCancelled.
function cancelCut(bytes32 _id) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _id | bytes32 | The operation id to cancel. |
emergencyRemoveCut
Zero-delay, REMOVAL-ONLY escape hatch: lets a guardian instantly unbind a compromised or
buggy facet's selectors WITHOUT scheduling and WITHOUT the Safe — and, by design, even
while the normal cut path is halted by EmergencyStop. Removal-only, frozen-protected, no
init delegatecall, and never scheduleCut/executeCut or a stop-recovery selector
(emergencyResume, removeGuardian, revokeRole; reverts
IEmergencyCut.EmergencyCutEntrypointProtected even with an empty frozen set). Recorded in
the SAME append-only registry and emits IEmergencyCut.EmergencyCutExecuted. Mirrors
GovernedDiamondCutLib.emergencyRemoveCut.
Liveness: a guardian can still trip the stop, which blocks scheduleCut and executeCut, and
can trip it again between the two. A stop only delays: a ready operation never expires, and
since the recovery selectors cannot be removed, the DEFAULT_ADMIN_ROLE holder can always
revoke a hostile guardian, resume, and let the Safe cut again to restore anything removed.
This assumes a live admin; with the admin role renounced a stop is permanent.
function emergencyRemoveCut(FacetCut[] calldata _cuts) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _cuts | FacetCut[] | The facet cuts to apply; every entry MUST be a Remove (facetAddress == address(0)). |
freezeSelectors
Permanently freezes _selectors. Gated to the pinned Safe. Append-only; no unfreeze.
function freezeSelectors(bytes4[] calldata _selectors) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _selectors | bytes4[] | 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.
function _enforceNotFrozen(GovernedSafeDiamondCutStorage storage $, FacetCut[] calldata _diamondCut) private view;Parameters
| Name | Type | Description |
|---|---|---|
| $ | GovernedSafeDiamondCutStorage | |
| _diamondCut | FacetCut[] |
getTimestamp
Returns the ready timestamp (eta) of an operation: 0 if never scheduled or cancelled, and
DONE_TIMESTAMP (1) once executed.
function getTimestamp(bytes32 _id) internal view returns (uint256);Parameters
| Name | Type | Description |
|---|---|---|
| _id | bytes32 | The operation id to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
isOperationPending
Returns whether _id is scheduled but not yet matured (eta in the future).
function isOperationPending(bytes32 _id) internal view returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _id | bytes32 | The operation id to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
isOperationReady
Returns whether _id is scheduled and matured (eta reached, not yet executed).
function isOperationReady(bytes32 _id) internal view returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _id | bytes32 | The operation id to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
isOperationDone
Returns whether _id has been executed. Never-scheduled and cancelled ids report false,
matching OpenZeppelin's TimelockController Done state.
function isOperationDone(bytes32 _id) internal view returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _id | bytes32 | The operation id to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
cutCount
Returns the number of cuts applied so far (also the latest registry version).
function cutCount() internal view returns (uint256);Returns
| Name | Type | Description |
|---|---|---|
| <none> | uint256 |
getCutRecord
Returns the immutable audit record for a given registry version.
function getCutRecord(uint256 _version) internal view returns (IUpgradeRegistry.CutRecord memory);Parameters
| Name | Type | Description |
|---|---|---|
| _version | uint256 | The registry version to look up. |
Returns
| Name | Type | Description |
|---|---|---|
| <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
| Name | Type | Description |
|---|---|---|
| _selector | bytes4 | The function selector to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |
frozenSelectors
Returns the full set of frozen selectors.
function frozenSelectors() internal view returns (bytes4[] memory);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bytes4[] |
previewCut
Pre-flight simulation of the frozen-selector guard against the current set, no mutation.
function previewCut(FacetCut[] calldata _cuts) internal view returns (bool ok, bytes4 offendingSelector);Parameters
| Name | Type | Description |
|---|---|---|
| _cuts | FacetCut[] | The candidate facet cuts to simulate. |
Returns
| Name | Type | Description |
|---|---|---|
| ok | bool | true if no Replace/Remove in _cuts targets a frozen selector. |
| offendingSelector | bytes4 | The 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.
function verifyInterfaceRegistered(bytes4 _interfaceId) internal view returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _interfaceId | bytes4 | The ERC-165 interface identifier to check. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool |