SafeDiamondCut
Inherits: ISafeDiamondCut, IUpgradeRegistry, IFrozenSelectors, IEmergencyCut
Title: SafeDiamondCut
Author: David Dada <daveproxy80@gmail.com> (https://github.com/dadadave80)
Stateless Diamond facet exposing the EIP-2535 diamondCut function at the canonical
selector 0x1f931c1c, gated behind EmergencyStop + a pinned Gnosis Safe multisig. Drop-in
replacement for diamond-lib's owner-gated DiamondCutFacet in a Safe-governed deployment.
Also serves the append-only on-chain IUpgradeRegistry of every executed cut and a
self-administered Safe-rotation entry point. This is the INSTANT (no-delay) variant; use
GovernedSafeDiamondCut when a mandatory timelock between approval and execution is wanted.
All logic lives in SafeDiamondCutLib. This contract is stateless and forwards its calls to the
library; inherit it in your Diamond to add Safe-gated upgrades. The registry getters plus the
frozen-selector / preview / verify / setSafe / safe surface are plain facet functions and are
NOT advertised as a distinct ERC-165 interface, so the facet's advertised id stays the canonical
cut selector 0x1f931c1c.
AUTHORITY: the Safe collects M-of-N owner signatures off-chain and verifies the threshold
on-chain in execTransaction, then calls this facet. The facet does NOT re-verify signatures;
it trusts solely that msg.sender == the pinned Safe. The Safe MUST dispatch with
operation = Call (NEVER DelegateCall): a DelegateCall would run this code in the Safe's own
context, where msg.sender is whoever called the Safe, defeating the gate.
Notes:
- lattice-version: 0.1.0
- lattice-source: Lattice original
Functions
diamondCut
Add/replace/remove any number of functions and optionally execute a function with
delegatecall — but only when not emergency-stopped and the caller is the pinned Safe.
The Safe MUST call with operation = Call.
function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata)
external
payable
virtual;Parameters
| Name | Type | Description |
|---|---|---|
| _diamondCut | FacetCut[] | The facet addresses, cut actions, and function selectors. |
| _init | address | The address of the contract or facet to delegatecall after the cut (0 to skip). |
| _calldata | bytes | The calldata passed to _init. |
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 (non-zero, real Safe whose threshold is at least the minimum
meaningful threshold of 1). Emits SafeRotated.
function setSafe(address _newSafe) external virtual;Parameters
| Name | Type | Description |
|---|---|---|
| _newSafe | address | The new Safe to pin as the cut authority. |
safe
Returns the currently pinned Safe authority.
function safe() external view virtual returns (address);Returns
| Name | Type | Description |
|---|---|---|
| <none> | address | The pinned Safe address. |
emergencyRemoveCut
Zero-delay, removal-only emergency cut: instantly unbinds the supplied selectors from
the diamond. Callable ONLY by an EMERGENCY_GUARDIAN_ROLE holder, and — unlike the
governed diamondCut — it INTENTIONALLY works while EmergencyStop is engaged (it is the
panic button). Every FacetCut must use action Remove (revert
EmergencyCutMustBeRemoveOnly otherwise); none may target the cut entrypoint (revert
EmergencyCutEntrypointProtected), a stop-recovery selector (same revert) or a frozen
selector (revert IFrozenSelectors.FrozenSelectorProtected); no init delegatecall is
permitted (nothing to initialize on a pure removal). The removal is recorded in the IUpgradeRegistry and
emits EmergencyCutExecuted.
function emergencyRemoveCut(FacetCut[] calldata _cuts) external virtual;Parameters
| Name | Type | Description |
|---|---|---|
| _cuts | FacetCut[] | The facet cuts to apply; every entry MUST be a Remove (facetAddress == address(0)). |
cutCount
Returns the number of governed cuts recorded so far, which is also the latest version.
function cutCount() external view virtual 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 CutRecord.
function getCutRecord(uint256 _version) external view virtual returns (IUpgradeRegistry.CutRecord memory);Parameters
| Name | Type | Description |
|---|---|---|
| _version | uint256 | The registry version to look up. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | IUpgradeRegistry.CutRecord |
freezeSelectors
Permanently marks selectors as frozen: once frozen, a selector can never be the
target of a Replace or Remove in any future governed cut. Append-only — there is
deliberately no unfreeze. Gated like the facet's cut: on GovernedDiamondCut, behind
UPGRADE_EXECUTOR_ROLE (the single-holder role that gates diamondCut), so in its recipes
only a timelock-relayed governance proposal can freeze (a co-cut self-call relay such as
AccessManager execute would also reach it); on SafeDiamondCut and
GovernedSafeDiamondCut, by the pinned Safe.
function freezeSelectors(bytes4[] calldata _selectors) external virtual;Parameters
| Name | Type | Description |
|---|---|---|
| _selectors | bytes4[] | The function selectors to freeze. Already-frozen selectors are idempotent. |
isSelectorFrozen
Returns whether selector is in the frozen set.
function isSelectorFrozen(bytes4 _selector) external view virtual returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _selector | bytes4 | The function selector to query. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool | true if the selector is frozen (protected from Replace/Remove). |
frozenSelectors
Returns the full set of frozen selectors.
function frozenSelectors() external view virtual returns (bytes4[] memory);Returns
| Name | Type | Description |
|---|---|---|
| <none> | bytes4[] | The frozen selectors, in insertion order (subject to swap-and-pop reordering on internal set mechanics, though the set is append-only so order is stable here). |
previewCut
Pre-flight simulation of the frozen-selector guard against the CURRENT frozen set, with
no state change and no broadcast. Lets an upgrade workflow fail fast — before proposing
— if a cut would touch a frozen selector. Mirrors exactly the check the guarded
diamondCut performs before applying: only Replace/Remove actions are inspected.
This is a pure read of the frozen set; it does NOT re-implement diamond-lib's collision / immutable-function / bytecode-existence checks, which fire at execution time.
function previewCut(FacetCut[] calldata _cuts) external view virtual 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.
A thin wrapper over the shared ERC-165 support read, so an upgrade workflow can confirm an
expected interface is still (or now) advertised after a cut. Equivalent to calling
supportsInterface(interfaceId) on the diamond.
function verifyInterfaceRegistered(bytes4 _interfaceId) external view virtual returns (bool);Parameters
| Name | Type | Description |
|---|---|---|
| _interfaceId | bytes4 | The ERC-165 interface identifier to check. |
Returns
| Name | Type | Description |
|---|---|---|
| <none> | bool | true if the interface is advertised. |
exportSelectors
ERC-8153 selector export: this facet's cuttable selectors, tightly packed (4 bytes each).
Excludes exportSelectors() itself (0x0ef22643) - it is never cut into a diamond. Order matches
forge inspect SafeDiamondCut methodIdentifiers (alphabetical by signature); kept in exact parity by
ExportSelectorsParityTest. Chunks:
cutCount() 0xaa982c45
diamondCut((address,uint8,bytes4[])[],address,bytes) 0x1f931c1c
emergencyRemoveCut((address,uint8,bytes4[])[]) 0xc83542a6
freezeSelectors(bytes4[]) 0x4487678f
frozenSelectors() 0x22cabf70
getCutRecord(uint256) 0x3adda78e
isSelectorFrozen(bytes4) 0xc8d8e114
previewCut((address,uint8,bytes4[])[]) 0x35342750
safe() 0x186f0354
setSafe(address) 0x5db0cb94
verifyInterfaceRegistered(bytes4) 0x0746a956
function exportSelectors() external pure virtual returns (bytes memory selectors);Returns
| Name | Type | Description |
|---|---|---|
| selectors | bytes |