Gas Optimized Diamond-2
Specifically this is a gas efficient implementation of the
diamondCut function. Adding/replacing/removing is optimized to take the least gas.
The loupe functions are NOT gas optimized. In this implementation the
facetAddress loupe function is gas efficient and can be called in on-chain transactions.
diamondCut implementation avoids storage read and writes. Fits 8 function selectors in a single storage slot. This is a gas optimization.
contracts/Diamond.sol file shows an example of implementing a diamond.
contracts/facets/DiamondCutFacet.sol file shows how to implement the
diamondCut external function.
contracts/facets/DiamondLoupeFacet.sol file shows how to implement the four standard loupe functions.
contracts/libraries/LibDiamond.sol file shows how to implement Diamond Storage.
test/diamondTest.js file gives tests for the
diamondCut function and the Diamond Loupe functions.
A simpler implementation of a diamond exists here: https://github.com/mudgen/diamond-1
To understand the code of this implementation it is recommended to first understand the diamond-1 implementation.
How to Get Started Making Your Diamond
The most important thing is reading and understanding the EIP-2535 Diamonds. If something is unclear let me know!
The second important thing is using an EIP-2535 Diamonds reference implementation. You are at the right place because this is the README for the gas-optimized reference implementation.
This diamond implementation is boilerplate code that makes a diamond compliant with EIP-2535 Diamonds.
The Diamond.sol contract could be used as is, or it could be used as a starting point and customized. This contract is the diamond. Its deployment creates a diamond. It's address is a stable diamond address that does not change.
The LibDiamond.sol library could be used as is. It shows how to implement Diamond Storage. This contract includes contract ownership which you might want to change if you want to implement DAO-based ownership or other form of contract ownership. Go for it. Diamonds can work with any kind of contract ownership strategy.
The LibDiamondCut.sol library contains an internal function version of
diamondCut that can be used in the constructor of a diamond or other places.
Calling Diamond Functions
In order to call a function that exists in a diamond you need to use the ABI information of the facet that has the function.
Here is an example that uses web3.js:
let myUsefulFacet = new web3.eth.Contract(MyUsefulFacet.abi, diamondAddress);
In the code above we create a contract variable so we can call contract functions with it.
In this example we know we will use a diamond because we pass a diamond's address as the second argument. But we are using an ABI from the MyUsefulFacet facet so we can call functions that are defined in that facet. MyUsefulFacet's functions must have been added to the diamond (using diamondCut) in order for the diamond to use the function information provided by the ABI of course.
Similarly you need to use the ABI of a facet in Solidity code in order to call functions from a diamond. Here's an example of Solidity code that calls a function from a diamond:
string result = MyUsefulFacet(diamondAddress).getResult()
Run the Unit Tests
Start the ganache-cli node by opening a terminal and typing:
npm run start
Once ganache-cli is launched, open another terminal and type:
npm run test
Get Help and Join the Community
- EIP-2535 Diamonds
- Introduction to EIP-2535 Diamonds
- Understanding Diamonds on Ethereum
- Solidity Storage Layout For Proxy Contracts and Diamonds
- New Storage Layout For Proxy Contracts and Diamonds
- Diamond Setter
- Upgradeable smart contracts using the EIP-2535 Diamonds
- buidler-deploy supports diamonds
This example diamond implementation was written by Nick Mudge.
MIT license. See the license file. Anyone can use or modify this software for their purposes.