Probably the biggest complaint over the years of ERC-2535 Diamonds has been the specialized terminology that was introduced by it. If you thought I took the diamond analogy too far by using too many diamond-industry-specific terms, you are right. I did. I admit it.
Fortunately the ERC-2535 Diamonds standard can be revised to improve this aspect. It is possible to change a final Ethereum standard.
EIP-1 says this:
A Final EIP exists in a state of finality and should only be updated to correct errata and add non-normative clarifications.
āNon-normative clarificationsā means explanations that do not change the rules of the standard. They clarify wording or improve understanding without altering any requirements or behaviors defined by the ERC.
The terms diamond and facet are good. The terms DiamondCut and loupe are not so good, and itās time to do something about it.
Analysis & Plans
The diamond analogy has been useful for conceptualizing the smart-contract architecture defined by ERC-2535. It gives developers a mental model for how a system can present a single unified interface while being internally composed of multiple parts that share data.
The analogy:
Physical diamond:
-
Facets are different appearances or faces of a physical diamond. They all share the same center of the diamond.
-
Facets are different appearances and functionality of an ERC-2535 diamond. They share the same Ethereum address and the same contract storage, which can be considered the ācenterā of a smart contract.
This analogy is imperfect, but useful.
Term: diamond
The term diamond (or diamond contract) is valuable because it communicates a specific type of proxy architecture. Simply calling it a āproxy contractā is too broadāevery standardized proxy has its own name, such as the UUPS proxy.
A diamond is a proxy contract that implements ERC-2535 Diamonds, also known as the diamond standard.
Is there an existing software term that captures what a diamond is?
No.
There are software concepts that resemble parts of the diamond architectureārouters, dynamic dispatch, virtual method tables, and even the Facade design pattern. Each of these describes partially how a diamond works, usually the mechanism for selecting or invoking code.
But none of them describe a system where:
- Multiple components share a single identity.
- All components operate on the same shared storage.
- Components can be added, replaced, or removed without changing that identity or losing state.
These three propertiesāshared identity, shared storage, and optional modular upgradeabilityādefine the diamond pattern. No existing software term captures all of them.
Verdict: diamond is good, keep it.
Term: facet:
The term āfacetā to describe an implementation contract for a diamond is absolutely useful. It is short, descriptive, and specific to diamonds. When working with smart contract systems that implement EIP-2535 Diamonds, the term āfacetā communicates immediately and clearly. Such systems have multiple facets, and some have many facets.
The term āfacetā is needed to distinguish contracts from others. āImplementation contractā is unwieldy and general.
Verdict: facet is good, keep it.
Term: loupe
In the diamond industry, a loupe is a small magnifying glass used to examine diamonds. In ERC-2535 Diamonds, a loupe is a set of four external functions that return information about what is inside a diamond ā what functions it provides and from which facets.
It might be clever or fun to use the term āloupe,ā but it adds unnecessary cognitive overhead for people learning or working with diamonds. It is unnecessary terminology, and some people strongly dislike it.
The established software term is introspection, and it already accurately describes the functionality.
I plan to revise the ERC-2535 Diamonds standard to replace the term āloupeā with introspection.
IDiamondLoupe ā IDiamondIntrospection.
The actual introspection function names:
facets()facetFunctionSelectors(address _facet)facetAddresses()facetAddress(bytes4 _functionSelector)
are already well named and will not change.
This is a non-normative change: removing āloupeā from ERC-2535 Diamonds does not change how diamonds work or behave. Existing implementations may update their documentation to replace āloupeā with āintrospectionā or add a note about the name change.
Verdict: remove the name loupe.
Term: Cut
diamondCut is the name of the optional function that is used to add/replace/remove functions in a diamond.
DiamondCut is the name of the event that is emitted when any functions are added or replaced or removed in a diamond.
The term ācutā (used in diamondCut and DiamondCut) is far less useful than the terms ādiamondā and āfacetā. In a physical diamond, facets are created by cutting the diamond. This analogy is unnecessary and adds cognitive overhead. No analogy is useful or needed to understand adding/replacing/removing functions in a diamond.
I want to replace the diamondCut upgrade function with upgradeDiamond. And I want to replace the DiamondCut event with DiamondUpgraded. I propose the following interfaces:
interface IDiamond {
enum Action {Add, Replace, Remove}
// Add=0, Replace=1, Remove=2
struct FacetChange {
address facetAddress;
Action action;
bytes4[] functionSelectors;
}
/**
* @notice Emitted when functions are added, replaced, or removed in a
* diamond.
* This event is emitted when a diamond is created (when facets
* are added for the first time) and during all subsequent upgrades.
*
* @dev Each FacetChange entry describes changes to a facet:
* - Add: Adds new function selectors from a facet.
* - Replace: Changes the facet that implements existing function
* selectors.
* - Remove: Deletes function selectors so the diamond no longer
* exposes them.
*
* The diamond uses these facet changes to update its internal
* selector-to-facet lookup table, enabling or altering the contract's
* external interface.
*
* @param _facetChanges The list of facet changes applied in this upgrade.
* Each entry specifies the facet address, the action
* taken, and the function selectors affected.
*
* @param _data Arbitrary data with no prescribed format. This may
* contain initialization or configuration data
* associated with the upgrade, or it may be empty
* if no additional processing is required.
*/
event DiamondUpgraded(FacetChange[] _facetChanges, bytes _data);
}
interface IUpgradeDiamond is IDiamond {
/**
* @notice Add, replace, remove any number of functions in a diamond.
* @dev This function is optional. Diamonds can be immutable or
* upgradeable.
* @param _facetChanges The facet changes to apply, including addresses,
* actions, and function selectors.
* @param _data Optional additional data with no specified format.
*/
function upgradeDiamond(
FacetChange[] calldata _facetChanges,
bytes calldata _data
) external;
}
Existing diamonds emitting DiamondCut will continue to work exactly as before, and will continue to be supported by the standard. Tools and explorers should support both event names.
Existing projects that have the diamondCut function can keep using it if they want to. Or they can use diamondCut to replace diamondCut with upgradeDiamond.
Since ERC-2535 Diamonds is a Final Ethereum standard, making such a change would require the overwhelming agreement and support of existing projects that have implemented the standard, as well as those that plan to adopt it. It would also require the support and agreement of tools that support the standard such as Louper, Etherscan, and others, as well as newer tools such as Compose and Herd.
Even then, I do not know if the EIP editors will allow the change. But if inheritance can be removed from Solidity, maybe anything can happen.
Or instead of making changes to the standard, should we just make a Diamond 2 standard?
I am interested in your feedback, your ideas, and your reactions, so please leave a comment.