Diamond Implementations
A diamond implementation implements EIP-2535 Diamonds. These are diamond reference implementations.
Diamonds enable you to build efficient, powerful, flexible, modular contract systems.
Every diamond implementation implements the following:
- diamondCut function Standard function used to add/replace/remove functions on a diamond.
- Loupe functions Four standard functions used to show what functions and facets a diamond has.
There are three diamond reference/example implementations that have different benefits and disadvantages in terms of code complexity and gas costs. Here is a breakdown of the differences between the three implementations. The ratings (high, medium, low) are in relation to each other.
Implementation | diamondCut complexity |
diamondCut gas cost |
loupe complexity |
loupe gas cost |
---|---|---|---|---|
diamond-1 | low | medium | medium | high |
diamond-2 | high | low | high | high |
diamond-3 | medium | high | low | low |
It is possible to choose one implementation and then in the future upgrade the diamond to switch to a different implementation.
All three implementations pass the same tests.
diamond-1 and diamond-2 use less gas to add/replace/remove functions.
diamond-3 uses less gas to call the diamond loupe functions.
diamond-1 and diamond-2 are implemented the same way except that diamond-2 is gas optimized. To understand how diamond-2 is implemented look at diamond-1 first.
Diamond Repositories
Links to diamond reference implementation repositories:
How diamond-1 is implemented
- Has an
bytes4[] selectors
array that stores the function selectors of a diamond. - Has a
mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition
mapping that maps each function selector to its facet address and its position in theselectors
array.
It's facets
, facetFunctionSelectors
, facetAddresses
loupe functions should not be called in on-chain transactions because their gas cost is too high. These functions should be called by off-chain software.
The facetAddress
loupe function has a low fixed gas cost in all implementations and can be called in on-chain transactions.
How diamond-2 is implemented
diamond-2 is implemented the same way as diamond-1 except that the selectors
array is implemented as a mapping of 32-byte storage slots and uses various gas-optimizations to reduce storage reads and writes.
This implementation is gas-optimized for adding/replacing/removing functions on a diamond.
It's facets
, facetFunctionSelectors
, facetAddresses
loupe functions should not be called in on-chain transactions because their gas cost is too high. These functions should be called by off-chain software.
The facetAddress
loupe function has a low gas cost in all implementations and can be called in on-chain transactions.
How diamond-3 is implemented
-
Has an
address[] facetAddresses
array that stores the facet addresses of a diamond. -
Has a
mapping(address => FacetFunctionSelectors) facetFunctionSelectors
mapping that maps each facet address to its array of function selectors and its position in thefacetAddresses
array. -
Has a
mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition
mapping that maps each function selector to its facet address and its position in thefacetFunctionSelectors[facetAddress].functionSelectors
array.
The standard loupe functions facets
, facetFunctionSelectors
, facetAddresses
CAN be called in on-chain transactions. Note that if a diamond has a great many functions and/or facets these functions may still cause an out-of-gas error.
The facetAddress
loupe function has a low fixed gas cost in all implementations and can be called in on-chain transactions.