A diamond is a proxy contract that delegatecalls to multiple implementation contracts called facets.
Diamond contracts were originally standardized by ERC-2535. This standard refines that specification by simplifying terminology, reducing the implementation complexity of introspection functions, and standardizing specific events.
This standard preserves the full capabilities of diamond contracts while reducing complexity. It also specifies an optional upgrade path for existing ERC-2535 diamonds.
Through a single contract address, a diamond provides functionality from multiple implementation contracts (facets). Each facet is independent, yet facets can share internal functions and storage. This architecture allows large smart-contract systems to be composed from separate facets and presented as a single contract, simplifying deployment, testing, and integration with other contracts, off-chain software, and user interfaces.
By decomposing large smart contracts into facets, diamonds can reduce complexity and make systems easier to reason about. Distinct areas of functionality can be isolated, organized, tested, and managed independently.
Diamonds combine the single-address convenience of a monolithic contract with the modular flexibility of distinct, integrated contracts.
This architecture is well suited to immutable smart-contract systems, where all functionality is composed from multiple facets at deployment time and permanently fixed thereafter.
For upgradeable systems, diamonds enable incremental development: new functionality can be added, and existing functionality modified, without redeploying unaffected facets.
Additional motivation and background for diamond-based smart-contract systems can be found in ERC-1538 and ERC-2535.
Unlike monolithic contracts, a diamond's external functions are commonly determined by runtime routing (selector → facet mapping) rather than being wholly represented in the diamond's source code or bytecode. To accurately determine or display the full set of functionality a diamond has, tooling must rely on standardized introspection functions and events.
Block explorers, indexers, development tools, and user interfaces need a standard way to inspect which functions and facets a diamond possesses. Additionally, tooling can be built to reconstruct and display the full development or upgrade history of diamond contracts using event logs.
ERC-2535 standardized introspection functions and events. ERC-8109 re-standardizes these to make diamond contracts easier to implement and easier to understand.
ERC-8109 improves upon ERC-2535 Diamonds in the following ways:
delegatecall, so reads/writes affect the diamond’s storage. The term facet is derived from the diamond industry, referring to a flat surface of a diamond.This diagram shows the structure of a diamond.
It shows that a diamond has a mapping from function to facet and that facets can access the storage inside a diamond.
When an external function is called on a diamond, its fallback function is executed. The fallback function determines which facet to call based on the first four bytes of the calldata (known as the function selector) and executes the function from the facet using delegatecall.
A diamond’s fallback function and delegatecall enable a diamond to execute a facet’s function as if it was implemented by the diamond itself. The msg.sender and msg.value values do not change and only the diamond’s storage is read and written to.
Here is an example of how a diamond’s fallback function might be implemented:
These events are REQUIRED.
For each function selector that is added, replaced, or removed, the corresponding event MUST be emitted.
delegatecallsWhen the standard upgradeDiamond function defined in this specification is implemented, it MUST emit DiamondDelegateCall as called for in that function. Otherwise this event is OPTIONAL.
This event can be used to record delegatecalls made by a diamond.
This event MUST NOT be emitted for delegatecalls made by a diamond’s fallback function when routing calls to facets. It is only intended for delegatecalls made by functions in facets or a diamond’s constructor.
When the standard upgradeDiamond function defined in this specification is implemented, it MUST emit DiamondMetadata as called for in that function. Otherwise this event is OPTIONAL.
This event can be used to record versioning or other information about diamonds.
It can be used to record information about diamond upgrades.
The following upgrade function is OPTIONAL.
This means two important things:
A diamond can be fully constructed within its constructor function without adding any upgrade function, making it immutable upon deployment.
A large immutable diamond can be built using well organized facets.
A diamond can initially be upgradeable, and later made immutable by removing its upgrade function.
Instead of, or in addition to the upgrade function specified below, you can design and create your own upgrade functions and remain compliant with this standard. All that is required is that you emit the appropriate add/replace/remove required events specified in the events section, and that the introspection functions defined in the Inspecting Diamonds section continue to accurately return function and facet information.
upgradeDiamond FunctionThis upgrade function is designed for interoperability with tools, such as GUIs and command line tools, which can be used to perform upgrades on diamonds. Its behavior is specified so that tooling and users can rely on consistent, predictable execution.
This upgrade function adds/replaces/removes any number of functions from any number of facets in a single transaction. In addition, it can optionally execute a function with delegatecall.
The upgradeDiamond function MUST adhere to the following implementation requirements:
The complete definitions of the events and custom errors referenced below are given earlier in this document.
Function Inputs
_addFunctions maps new selectors to their facet implementations._replaceFunctions updates existing selectors to new facet addresses._removeFunctions removes selectors from the diamond.Execution Order
Function Event Emission
DiamondFunctionAddedDiamondFunctionReplacedDiamondFunctionRemovedFunction Error Conditions
CannotAddFunctionToDiamondThatAlreadyExistsCannotReplaceFunctionThatDoesNotExistCannotReplaceFunctionWithTheSameFacetCannotRemoveFunctionThatDoesNotExistFacet Validation
_addFunctions or _replaceFunctions contains no contract bytecode, execution MUST revert with NoBytecodeAtAddress._addFunctions or _replaceFunctions but no function selectors are given with it, execution MUST revert with NoSelectorsProvidedForFacet.Delegate Validation
_delegate is non-zero but contains no contract code, then revert with the NoBytecodeAtAddress error.Delegatecall Execution
_delegate is non-zero, the diamond MUST execute _functionCall on _delegate using delegatecall.delegatecall fails and returns revert data, the diamond MUST revert with the same revert data.delegatecall fails and returns no revert data, the diamond MUST revert with DelegateCallReverted.delegatecall is performed, the diamond MUST emit the DiamondDelegateCall event._functionCall MAY be empty. If empty, the delegatecall executes with no calldata.Metadata Event
_tag is non-zero or _metadata.length > 0, the diamond MUST emit the DiamondMetadata event.After adding/replacing/removing functions a delegatecall can be done to initialize, modify, or remove state after an upgrade.
It is also possible to call this function just to perform a delegatecall to modify a diamond's state without adding/replacing/removing any functions.
A reference implementation of this function is provided in DiamondUpgradeFacet.sol.
Diamond introspection functions return information about what functions and facets are used in a diamond.
These functions MUST be implemented and are required by the standard:
The essence of a diamond is its function -> facet mapping. functionFacetPairs() returns that mapping as an array of (selector, facet) pairs.
These functions were chosen because they provide all necessary facet and function data about a diamond. They are very simple to implement and are computationally efficient.
Block explorers, GUIs, tests, and other tools may rely on their presence.
A reference implementation exists for these introspection functions here: DiamondInspectFacet.sol
Other introspection functions may be added to a diamond. The above two functions are the only ones required by this standard.
A diamond MUST implement the following:
fallback() function.delegatecall.DiamondFunctionAdded — when a function is added to a diamond.DiamondFunctionReplaced — when a function is replaced in a diamond.DiamondFunctionRemoved — when a function is removed from a diamond.facetAddress(bytes4 _functionSelector)functionFacetPairs()receive() functionA diamond MAY have a receive() function.
Definition:
An immutable function is an external or public function defined directly in a diamond contract, not in a facet.
This definition does not apply to a diamond's constructor or the specialfallback()andreceive()functions.
A diamond can have zero or more immutable functions.
A diamond with immutable functions has the following additional requirements that MUST be followed:
DiamondFunctionAdded event MUST be emitted for each immutable function.facetAddress(bytes4 _functionSelector) and functionFacetPairs(), where the facet address is the diamond’s own address.error CannotReplaceImmutableFunction(bytes4 _selector);error CannotRemoveImmutableFunction(bytes4 _selector);This standard provides standard events, an upgrade function and introspection functions so that GUIs, block explorers, command line programs, and other tools and software can detect and interoperate with diamond contracts.
Software can retrieve function selectors and facet addresses from a diamond in order to use and show what functions a diamond has. Function selectors and facet addresses, combined with contract ABIs and verified source code, provide sufficient information for tooling and user interfaces.
This standard is a simplification and refinement of ERC-2535 Diamonds.
A diamond compliant with ERC-8109 is NOT required to implement ERC-2535.
Here are changes in ERC-8109 Diamonds:
Routing calls via delegatecall introduces a small amount of gas overhead. In practice, this cost is mitigated by several architectural and tooling advantages enabled by diamonds:
Optional, gas-optimized functionality
By structuring functionality across multiple facets, diamonds make it straightforward to include specialized, gas-optimized features without increasing the complexity of core logic.
For example, an ERC-721 diamond may implement batch transfer functions in a dedicated facet, improving both gas efficiency and usability while keeping the base ERC-721 implementation simple and well-scoped.
Reduced external call overhead
Some contract architectures require multiple external calls within a single transaction. By consolidating related functionality behind a single diamond address, these interactions can execute internally with shared storage and shared authorization, reducing gas costs from external calls and repeated access-control checks.
Selective optimization per facet
Because facets are compiled and deployed independently, they may be built with different compiler optimizer settings. This allows gas-critical facets to use aggressive optimization configurations to reduce execution costs, without increasing bytecode size or compilation complexity for unrelated functionality.
functionFacetPairs() Gas UsageThe functionFacetPairs() function is meant to be called off-chain. At this time major RPC providers have a maximum gas limit of about 550 million gas. Gas benchmark tests show that the functionFacetPairs() function can return 60,000 (selector, facet) pairs using less gas than that.
ERC-8109 implementations are free to add iteration or pagination-based introspection functions, but they are not required by this standard.
Diamonds and facets need to use a storage layout organizational pattern because Solidity’s default storage layout doesn’t support proxy contracts or diamonds. The storage layout technique or pattern to use is not specified in this ERC. However, examples of storage layout patterns that work with diamonds are ERC-8042 Diamond Storage and ERC-7201 Namespaced Storage Layout.
Facets are separately deployed, independent units, but can share state and functionality in the following ways:
A deployed facet can be used by many diamonds.
It is possible to create and deploy a set of facets that are reused by different diamonds.
The ability to use the same deployed facets for many diamonds has the potential to reduce development time, increase reliability and security, and reduce deployment costs.
It is possible to implement facets in a way that makes them usable/composable/compatible with other facets.
A function signature is the name of a function and its parameter types. Example function signature: myfunction(uint256). A limitation is that two external functions with the same function signature can’t be added to the same diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature.
Immutable functions offer minor gas savings by avoiding fallback logic and a delegatecall. However, they introduce a second implementation alongside facets, resulting in two ways to provide similar functionality. This increases implementation complexity and cognitive overhead.
A diamond is simpler to implement and understand without immutable functions.
In upgradeable diamonds, immutable functions reduce flexibility, as they cannot be replaced or removed. This limits the ability to evolve, fix, or improve functionality over time.
Immutable functions that read from or write to storage are not isolated from upgrades. Other functions, including upgrade logic, may modify the same storage relied upon by immutable functions.
Existing, deployed ERC-2535 Diamonds implementations MAY upgrade to this standard by performing an upgrade that does the following:
functionFacetPairs() introspection function.DiamondFunctionAdded event for every function currently in the diamond, including the new upgrade function and the new functionFacetPairs() function.After this upgrade, the diamond is considered compliant with this standard and SHOULD be indexed and treated as a diamond of this standard going forward.
This upgrade acts as a 'state snapshot'. Indexers only interested in the current state of the diamond can start indexing from this transaction onwards, without needing to parse the legacy DiamondCut history.
To reconstruct the complete upgrade history requires retrieving all the past DiamondCut events as well as all new events defined in this standard.
An ERC-2535 diamond that upgrades to this standard and has immutable functions MUST comply with the Immutable Functions section of the Specification.
If the ERC-2535 diamond upgrade function is immutable, then it can't be removed. If possible, disable the upgrade function by making its authentication always fail.
upgradeDiamond function is here: DiamondUpgradeFacet.sol.facetAddress(bytes4 _functionSelector) and functionFacetPairs() introspection functions is here: DiamondInspectFacet.solThe design and implementation of diamond ownership/authentication is not part of this standard.
It is possible to create many different authentication or ownership schemes with diamonds. Authentication schemes can be very simple or complex, fine grained or coarse. This proposal does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to add/replace/remove certain functions.
The development of standards and implementations of ownership, control and authentication of diamonds is encouraged.
upgradeDiamondThe upgradeDiamond function allows arbitrary execution with access to the diamond’s storage (through delegatecall). Access to this function must be restricted carefully.
A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with a standard upgradeDiamond function because it prevents adding function selectors that already exist.
A diamond emits an event every time a function is added, replaced or removed. Source code can be verified. This enables people and software to monitor changes to a diamond.
Security and domain experts can review a diamond’s upgrade history.
Copyright and related rights waived via CC0.