Compliance with EIP-2535 Diamonds Standard
The work on the EIP-2535 Diamonds standard occurred over a 4 year period that began with an earlier standard ERC-1538. Much work, research, feedback and experience went into optimizing the standard to be the best solution for what it does.
The standard was designed to be as flexible as possible but at the same time enable interoperability. Standardization to enable interoperability is what standards are for. The standard was also optimized from experience making robust and secure implementations of it. It is the way it is for good reasons.
There are several reference implementations of EIP-2535 on github. And there is a reference implementation of the standard embedded directly in the standard. These implementations are audited and currently being used by many projects.
It is important to know that the reference implementations are not the standard. No implementation is the standard. I say this because the standard is flexible and other ways to implement the standard are possible, and people can take existing implementations and modify them or add to them and still be in compliance with the EIP-2535 standard. I encourage people to build on top of the standard and do new things while keeping compliance with the standard.
To be in compliance with the EIP-2535 Diamonds standard it is only necessary to implement a short list of things which are stated in the standard. Before I give you the list it is important to understand what an âimmutable functionâ is. An immutable function is an external function defined directly in a diamond proxy contract, or is inherited by a diamond proxy contract.
A diamond proxy contract is the contract with the fallback function that delegatecalls to facets. It is the diamond.
Here are the only things that must be implemented for a smart contract to be in compliance with EIP-2535 and be a diamond. This is copied directly from the Implementation Points section of the EIP-2535 standard:
A diamond must implement the following:
A diamond contains a fallback function and zero or more immutable functions that are defined within it.
A diamond associates function selectors with facets.
When a function is called on a diamond it executes immediately if it is an âimmutable functionâ defined directly in the diamond. Otherwise the diamondâs fallback function is executed. The fallback function finds the facet associated with the function and executes the function using delegatecall. If there is no facet for the function then optionally a default function may be executed. If there is no facet for the function and no default function and no other mechanism to handle it then execution reverts.
Each time functions are added, replaced or removed a DiamondCut event is emitted to record it.
A diamond implements the IDiamondLoupe interface.
All immutable functions must be emitted in the DiamondCut event as new functions added. And the loupe functions must return information about immutable functions if they exist. The facet address for an immutable function is the diamondâs address. Any attempt to delete or replace an immutable function must revert.
Thatâs it, done.
To understand why immutable functions must be emitted in the DiamondCut event and returned by the loupe functions read this article: Compliance and Transparency of Immutable Functions in Diamonds
If any of the above six things are unclear to you then I suggest reading the EIP-2535 standard from the beginning. Also, the other articles on this website are helpful in understanding many aspects of the standard, like delegatecall, and how storage works, etc. If you still have questions then I suggest asking them in the Diamond Discord.
diamondCut
Upgrade Function is Optional
Note that the diamondCut
function that is used to upgrade diamonds, to add/replace/remove functions in diamonds, is optional. You can even make your own upgrade functions, but if you do that it is recommended (not required) to also implement diamondCut
to enable interoperability with current and future tooling.
The EIP-2535 standard says this about IDiamondCut, which contains the diamondCut
external function:
Diamonds should implement the
IDiamondCut
interface if after their deployment they allow modifications to their function selector mapping.
The language there is âshouldâ, not âmustâ, which means you donât have to have a diamondCut
external function to be in compliance with the standard.
Static Diamond Implementation Example
An example of an EIP-2535 Diamonds implementation that is very different from the reference implementations but still complies with the standard is here: https://etherscan.io/address/0x0000000001e4ef00d069e71d6ba041b0a16f7ea0#code#F34#L1
That static implementation uses hardcoded function selectors and addresses instead of storing and retrieving them from contract storage. The implementations of the loupe functions were generated by a program that found all the external functions used by the diamond and generated the code with the hardcoded values, including a binary search to gas-efficiently find any facet address for a given function selector.