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 import and use them in facets.
It is a common misunderstanding to think that all Solidity libraries are independently 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.
A video that shows how to use internal functions from Solidity libraries in facets of diamonds is here.
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. Internal functions defined with the
virtual
keyword can be overriden so do not use that keyword with internal functions shared between facets.A type safe way to call an external function defined in another facet is to do this: `MyOtherFacet(address(this)).myFunction(arg1, arg2)`. Note that this is a regular external function call so `mgs.sender` will change.
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:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; 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, bytes memory 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.
Hey Nick, in your gas efficient external call example you have "(bool success, uint result)". I think that might want it to be "(bool success, bytes memory result)". Thanks for the great article!