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.
Only internal functions actually used from a 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.
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:
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.