How to Share Functions Between Facets of a Diamond
Nick Mudge
Posted on March 31, 2021
There are a number of ways to share or reuse functions between facets of an Ethereum diamond. The best way I have found is to write internal functions in Solidity libraries and call those functions in facets.
It is a common misunderstanding to think that all Solidity libraries are deployed. Only Solidity libraries with external functions must be deployed. Solidity libraries with only internal functions are not deployed -- they are added to the bytecode of contracts/facets that use them.
Also, only the bytecode from the internal functions that are actually used from a Solidity library are added to the bytecode of a facet. So it is possible to have large Solidity libraries that are utilized across facets without adding too much to the bytecode of facets that use them.
It is possible to use diamond storage and/or AppStorage in Solidity libraries which makes libraries even more useful and powerful.
Checkout Aavegotchi's libraries which are used across facets.
In addition to using internal functions in Solidity libraries EIP-2535 Diamonds describes a number of other ways functions can be shared or reused between facets:
- Write internal functions in Solidity libraries and call those functions in facets. These functions can access diamond storage or AppStorage directly.
- Copy internal function code in one facet to the other facet.
- Put common internal functions in a contract that is inherited by multiple facets.
- A type safe way to call an external function defined in another facet is to do this:
MyOtherFacet(address(this)).myFunction(arg1, arg2)
-
A more gas-efficient way to call an external function defined in another facet is to use delegatecall. Here is an example of doing that:
DiamondStorage storage ds = diamondStorage(); bytes4 functionSelector = bytes4(keccak256("myFunction(uint256)")); // get facet address of function address facet = ds.selectorToFacet[functionSelector]; bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); (bool success, uint result) = address(facet).delegatecall(myFunctionCall); require(success, "myFunction failed");
Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it.
Posted on March 31, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.