SafeDiamondCutLib
Title: SafeDiamondCut 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, then delegates the actual cut to DiamondLib.diamondCut. Introduces no new cut
logic. The instant (no-delay) Safe-gated analogue of GovernedDiamondCutLib.
Three-layer pattern: this library holds all logic and the namespaced storage. The stateless
SafeDiamondCut 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 therefore does NOT re-verify signatures — it only trusts msg.sender == $._safe. The
Safe MUST call with operation = Call; a DelegateCall would execute this facet's code in the
Safe's own context (wrong msg.sender, wrong storage) and is unsupported.
Functions
safeDiamondCutStorage
Returns a storage reference to the SafeDiamondCutStorage struct.
function safeDiamondCutStorage() internal pure returns (SafeDiamondCutStorage storage $);Returns
| Name | Type | Description |
|---|---|---|
| $ | SafeDiamondCutStorage |
__SafeDiamondCut_init
Initializes the SafeDiamondCut module by pinning the Safe authority.
Validates _safe is non-zero and is a real, sensibly-configured Safe whose on-chain
getThreshold() meets _minThreshold (which must itself be > 0). Reverts
ISafeAuthority.SafeDiamondCutZeroSafe, ISafeAuthority.SafeDiamondCutZeroThreshold, or
ISafeAuthority.SafeDiamondCutThresholdTooLow otherwise. Must be called inside the
preInitializer/postInitializer window. Does NOT register an ERC-165 interface: the canonical
cut selector 0x1f931c1c is already registered by DiamondLib.registerInterface() (see the
ERC165_MAP_ICUT_SLOT note above).
function __SafeDiamondCut_init(address _safe, uint256 _minThreshold) 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. |
_checkSafe
Reverts ISafeAuthority.SafeDiamondCutUnauthorized unless the caller is the pinned Safe.
Library internal functions inline into the calling facet, so msg.sender here is the
original caller of the Diamond — i.e. the Safe when it dispatches with operation = Call.
function _checkSafe(SafeDiamondCutStorage storage $) private view;Parameters
| Name | Type | Description |
|---|---|---|
| $ | SafeDiamondCutStorage |
_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, so only the existing multisig can hand authority to its successor.
_newSafe is validated exactly like the init Safe, re-asserting getThreshold() >= 1 (the
minimum-meaningful threshold). Emits ISafeAuthority.SafeRotated.
function setSafe(address _newSafe) internal;Parameters
| Name | Type | Description |
|---|---|---|
| _newSafe | address | The new Safe to pin as the cut authority. |
safe
Returns the currently pinned Safe authority.
function safe() internal view returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address |
diamondCut
Guarded diamond cut: reverts if emergency-stopped, then requires the caller to be the
pinned Safe, 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
| 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. |
emergencyRemoveCut
Zero-delay, REMOVAL-ONLY escape hatch: lets a guardian instantly unbind a compromised or
buggy facet's selectors WITHOUT going through the Safe — 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 the Safe authority), can never remove diamondCut itself or a
stop-recovery selector (emergencyResume, removeGuardian, revokeRole; even when
unfrozen), and can never remove a frozen selector.
Guard ordering mirrors GovernedDiamondCutLib.emergencyRemoveCut: (1) guardian-only
authority; (2) NO checkNotStopped() — this is the panic button and must work while
stopped; (3) removal-only (any Add/Replace reverts IEmergencyCut.EmergencyCutMustBeRemoveOnly);
(4) entrypoint protection (diamondCut and EmergencyStopLib.isRecoverySelector revert
IEmergencyCut.EmergencyCutEntrypointProtected) and frozen protection; (5) NO init
delegatecall. Recorded in the SAME append-only registry and additionally emits
IEmergencyCut.EmergencyCutExecuted.
Liveness: a guardian can still trip the stop, which blocks diamondCut. 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: each becomes ineligible to be the target of a
Replace/Remove in any future cut. Gated to the pinned Safe — the same authority that
gates diamondCut. Append-only: already-frozen selectors are idempotent no-ops, and
there is 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(SafeDiamondCutStorage storage $, FacetCut[] calldata _diamondCut) private view;Parameters
| Name | Type | Description |
|---|---|---|
| $ | SafeDiamondCutStorage | |
| _diamondCut | FacetCut[] |
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.
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
| 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, with no
state change. Only Replace/Remove actions are inspected; a pure read of the frozen
set (does NOT re-implement diamond-lib's collision/immutable/bytecode checks).
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.
Thin wrapper over the shared ERC-165 support read. Equivalent to supportsInterface.
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 |