Module providing the internal functions to extend the diamond's upgrade functionality by adding, replacing, or removing facets.
Manages facet lifecycle (add, replace, remove) within a diamond.
Updates selector routing so subsequent calls dispatch to the new facet.
You can use modules to wrap around you own project logic while using the default Compose building blocks. Modules provides all the necessary internal helpers for maximum integration
Storage follows the diamond slot layout in this file; any code using the same STORAGE_POSITION or related positions reads and writes shared state.
See Facets & Modules for more information.
Storage
State Variables
Property Type Description DIAMOND_STORAGE_POSITIONbytes32Diamond storage slot position for this module (Value: keccak256("erc8153.diamond"))
Diamond Storage
Definition
struct DiamondStorage { mapping ( bytes4 functionSelector => FacetNode ) facetNodes ; FacetList facetList ; } struct FacetList { bytes4 headFacetNodeId ; bytes4 tailFacetNodeId ; uint32 facetCount ; uint32 selectorCount ; } struct FacetNode { address facet ; bytes4 prevFacetNodeId ; bytes4 nextFacetNodeId ; }
FacetReplacement
This struct is used to replace old facets with new facets.
Definition
struct FacetReplacement { address oldFacet ; address newFacet ; }
Functions
getDiamondStorage
function getDiamondStorage ( ) pure returns ( DiamondStorage storage s ) ;
Returns:
Property Type Description sDiamondStorageThe DiamondStorage struct in storage.
upgradeDiamond
Upgrade the diamond by adding, replacing, and/or removing facets.
Execution order:
1
Add Facets
Attach new facet contracts and register their function selectors.
2
Replace Facets
Swap existing selectors to point to updated facet implementations.
3
Remove Facets
Unregister selectors that are no longer needed in the diamond.
Then, if _delegate != address(0), the diamond performs a delegatecall with _delegateCalldata and emits DiamondDelegateCall.
function upgradeDiamond ( address [ ] calldata _addFacets , FacetReplacement [ ] calldata _replaceFacets , address [ ] calldata _removeFacets , address _delegate , bytes calldata _delegateCalldata , bytes32 _tag , bytes calldata _metadata ) external ;
Parameters:
Property Type Description _addFacetsaddress[]Facet addresses to add _replaceFacetsFacetReplacement[](oldFacet, newFacet) pairs to replace_removeFacetsaddress[]Facet addresses to remove _delegateaddressOptional contract to delegatecall (address(0) to skip) _delegateCalldatabytesOptional calldata to execute on _delegate _tagbytes32Optional arbitrary metadata, such as release version _metadatabytesOptional arbitrary metadata
Facets must implement exportSelectors() in order to make their selectors discoverable by diamonds.
Only the exported selectors will be added to the diamond.
interface IFacet { function exportSelectors ( ) external pure returns ( bytes memory ) ; } See Facet-Based Diamond EIP-8153 for more details.
addFacets
Adds new facets to a diamond and maps their exported selectors to the facet address.
Behavior:
1
Import selectors
Each facet in _facets is queried via exportSelectors().
2
Validate uniqueness
All exported selectors must be new to the diamond.
3
Link facet node
The first selector of each facet becomes the facet linked-list node id.
4
Emit event
Emits FacetAdded once per successfully added facet.
function addFacets ( address [ ] calldata _facets ) ;
Parameters:
Property Type Description _facetsaddress[]Facet addresses to add. Each facet must implement exportSelectors().
removeFacets
Removes facets from a diamond and unregisters the selectors they previously exported.
Behavior:
1
Validate facet
Checks the first exported selector currently maps to that facet.
2
Unlink facet
Removes the facet from the facet linked list.
3
Delete selectors
Deletes all selectors exported by the facet.
4
Emit event
Emits FacetRemoved once per removed facet.
function removeFacets ( address [ ] calldata _facets ) ;
Parameters:
Property Type Description _facetsaddress[]Facet addresses to remove.
replaceFacets
Replaces facets in a diamond using (oldFacet, newFacet) pairs.
Behavior:
1
Compare selector sets
Replacement is selector-set based, not just an address swap.
2
Update shared selectors
Selectors present in both facets are re-routed to newFacet.
3
Add new selectors
Selectors present only in newFacet are added.
4
Remove stale selectors
Selectors present only in oldFacet are removed.
5
Emit event
Emits FacetReplaced(oldFacet, newFacet) for each replacement pair.
function replaceFacets ( FacetReplacement [ ] calldata _replaceFacets ) ;
Parameters:
Property Type Description _replaceFacetsFacetReplacement[](oldFacet, newFacet) pairs to replace old with new.
importSelectors
Retrieves the function selectors exposed by a facet by calling its exportSelectors(). Validates the returned ABI-encoded bytes (offset, length, and that the payload length is a multiple of 4) and returns the packed selectors without copying (zero-copy decode).
Used internally by addFacets , replaceFacets and removeFacets .
function importSelectors ( address _facet ) view returns ( bytes memory selectors ) ;
Parameters:
Property Type Description _facetaddressContract address implementing exportSelectors() that returns ABI-encoded bytes of 4-byte function selectors.
Returns:
Property Type Description selectorsbytesPacked 4-byte function selectors (length is a multiple of 4). Same memory layout as returned by exportSelectors(); may point into the staticcall return buffer.
Returns the 4-byte function selector at the given index in a packed bytes array of selectors (e.g. from importSelectors or other selector packing).
function at ( bytes memory selectors , uint256 index ) pure returns ( bytes4 selector ) ;
Parameters:
Property Type Description selectorsbytesPacked array of 4-byte function selectors (length must be a multiple of 4). indexuint256Zero-based index of the selector to read (0 = first selector, 1 = second, etc.).
Returns:
Property Type Description selectorbytes4The function selector at the given index.
Events
Emitted when a facet is added to a diamond.
The function selectors this facet handles can be retrieved by calling IFacet(_facet).exportSelectors()
Signature: event FacetAdded ( address indexed _facet ) ; Parameters: Property Type Description _facetaddressThe address of the facet that handles function calls to the diamond.
Emitted when a facet is removed from a diamond.
The function selectors this facet handles can be retrieved by calling IFacet(_facet).exportSelectors()
Signature: event FacetRemoved ( address indexed _facet ) ; Parameters: Property Type Description _facetaddressThe address of the facet that previously handled function calls to the diamond.
Emitted when an existing facet is replaced with a new facet.
Selectors that are present in the new facet but not in the old facet are added to the diamond.
Selectors that are present in both the new and old facet are updated to use the new facet.
Selectors that are not present in the new facet but are present in the old facet are removed from the diamond.
The function selectors handled by these facets can be retrieved by calling:
IFacet(_oldFacet).exportSelectors()
IFacet(_newFacet).exportSelectors()
Signature: event FacetReplaced ( address indexed _oldFacet , address indexed _newFacet ) ; Parameters: Property Type Description _oldFacetaddressThe address of the facet that previously handled function calls to the diamond. _newFacetaddressThe address of the facet that now handles function calls to the diamond.
Emitted when a diamond's constructor function or function from a facet makes a delegatecall.
Signature: event DiamondDelegateCall ( address indexed _delegate , bytes _delegateCalldata ) ; Parameters: Property Type Description _delegateaddressThe contract that was delegatecalled. _delegateCalldatabytesThe function call, including function selector and any arguments.
Emitted to record information about a diamond. This event records any arbitrary metadata.
The format of _tag and _data are not specified by the standard.
Signature: event DiamondMetadata ( bytes32 indexed _tag , bytes _data ) ; Parameters: Property Type Description _tagbytes32Arbitrary metadata, such as a release version. _databytesArbitrary metadata.
Errors
Thrown by addFacets when a selector exported by a facet already exists in the diamond.
Signature: error CannotAddFunctionToDiamondThatAlreadyExists ( bytes4 _selector ) ; Thrown by removeFacets when the facet being removed is not currently part of the diamond.
Signature: error CannotRemoveFacetThatDoesNotExist ( address _facet ) ; Thrown by replaceFacets when a replacement pair provides the same address for both oldFacet and newFacet.
Signature: error CannotReplaceFacetWithSameFacet ( address _facet ) ; Thrown by replaceFacets when a selector in newFacet is already owned by a facet other than the specified oldFacet.
Signature: error CannotReplaceFunctionFromNonReplacementFacet ( bytes4 _selector ) ; Thrown by replaceFacets when the provided oldFacet does not match the facet currently registered in the diamond.
Signature: error FacetToReplaceDoesNotExist ( address _oldFacet ) ; Thrown by upgradeDiamond when the optional post-upgrade delegatecall fails without returning revert data.
Signature: error DelegateCallReverted ( address _delegate , bytes _delegateCalldata ) ; Thrown by importSelectors when the staticcall to _facet.exportSelectors() fails.
Signature: error ExportSelectorsCallFailed ( address _facet ) ; Thrown by importSelectors when the facet returns data that is not valid ABI-encoded bytes (e.g. wrong offset, length not a multiple of 4, or length exceeds payload).
Signature: error IncorrectSelectorsEncoding ( address _facet ) ; Thrown by importSelectors when the facet address has no code (e.g. EOA or uninitialized contract).
Signature: error NoBytecodeAtAddress ( address _contractAddress ) ; Thrown by importSelectors when the facet returns fewer than 4 bytes of selectors (i.e. no valid selector).
Signature: error NoSelectorsForFacet ( address _facet ) ;
Best Practices
Ensure all diamond upgrade operations are performed by authorized wallet.
Always verify facet logic contracts are immutable and trusted before using it inside a diamond.
Ensure each facet’s exportSelectors() returns a valid packed selector list in deterministic order.
Carefully audit any optional post-upgrade delegatecall (contract + calldata).
Security Considerations
This module doesn't provide any access control logic , when using it into your own facet, it is your responsibility to implement proper checks.
The upgradeDiamond function is critical: this function mutates the diamond’s selector routing and facet list.
That being said, make sure to provide verified and trusted contract addresses to avoid any unwanted changes or vulnerabilities
Integration Notes
This module interacts directly with the diamond's shared storage at the DIAMOND_STORAGE_POSITION, which is identified by keccak256("erc8153.diamond"). All functions within this module read from and write to this shared storage.
Changes made through upgradeDiamond, addFacets, replaceFacets, and removeFacets are immediately visible to the diamond and all facet that access the shared storage.
Last updated: March 13, 2026