590 lines
25 KiB
Solidity
590 lines
25 KiB
Solidity
|
// SPDX-License-Identifier: CC0-1.0
|
||
|
pragma solidity ^0.8.0;
|
||
|
|
||
|
/******************************************************************************\
|
||
|
* Author: Nick Mudge <nick@perfectabstractions.com>, Twitter/Github: @mudgen
|
||
|
* EIP-2535 Diamonds
|
||
|
/******************************************************************************/
|
||
|
|
||
|
// NOTE:
|
||
|
// To see the various things in this file in their proper directory structure
|
||
|
// please download the zip archive version of this reference implementation.
|
||
|
// The zip archive also includes a deployment script and tests.
|
||
|
|
||
|
interface IDiamond {
|
||
|
enum FacetCutAction {Add, Replace, Remove}
|
||
|
// Add=0, Replace=1, Remove=2
|
||
|
|
||
|
struct FacetCut {
|
||
|
address facetAddress;
|
||
|
FacetCutAction action;
|
||
|
bytes4[] functionSelectors;
|
||
|
}
|
||
|
|
||
|
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
|
||
|
}
|
||
|
|
||
|
interface IDiamondCut is IDiamond {
|
||
|
/// @notice Add/replace/remove any number of functions and optionally execute
|
||
|
/// a function with delegatecall
|
||
|
/// @param _diamondCut Contains the facet addresses and function selectors
|
||
|
/// @param _init The address of the contract or facet to execute _calldata
|
||
|
/// @param _calldata A function call, including function selector and arguments
|
||
|
/// _calldata is executed with delegatecall on _init
|
||
|
function diamondCut(
|
||
|
FacetCut[] calldata _diamondCut,
|
||
|
address _init,
|
||
|
bytes calldata _calldata
|
||
|
) external;
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
// LibDiamond
|
||
|
// LibDiamond defines the diamond storage that is used by this reference
|
||
|
// implementation.
|
||
|
// LibDiamond contains internal functions and no external functions.
|
||
|
// LibDiamond internal functions are used by DiamondCutFacet,
|
||
|
// DiamondLoupeFacet and the diamond proxy contract (the Diamond contract).
|
||
|
|
||
|
error NoSelectorsGivenToAdd();
|
||
|
error NotContractOwner(address _user, address _contractOwner);
|
||
|
error NoSelectorsProvidedForFacetForCut(address _facetAddress);
|
||
|
error CannotAddSelectorsToZeroAddress(bytes4[] _selectors);
|
||
|
error NoBytecodeAtAddress(address _contractAddress, string _message);
|
||
|
error IncorrectFacetCutAction(uint8 _action);
|
||
|
error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector);
|
||
|
error CannotReplaceFunctionsFromFacetWithZeroAddress(bytes4[] _selectors);
|
||
|
error CannotReplaceImmutableFunction(bytes4 _selector);
|
||
|
error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector);
|
||
|
error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector);
|
||
|
error RemoveFacetAddressMustBeZeroAddress(address _facetAddress);
|
||
|
error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector);
|
||
|
error CannotRemoveImmutableFunction(bytes4 _selector);
|
||
|
error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);
|
||
|
|
||
|
library LibDiamond {
|
||
|
bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
|
||
|
|
||
|
struct FacetAddressAndSelectorPosition {
|
||
|
address facetAddress;
|
||
|
uint16 selectorPosition;
|
||
|
}
|
||
|
|
||
|
struct DiamondStorage {
|
||
|
// function selector => facet address and selector position in selectors array
|
||
|
mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition;
|
||
|
bytes4[] selectors;
|
||
|
mapping(bytes4 => bool) supportedInterfaces;
|
||
|
// owner of the contract
|
||
|
address contractOwner;
|
||
|
}
|
||
|
|
||
|
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
|
||
|
bytes32 position = DIAMOND_STORAGE_POSITION;
|
||
|
assembly {
|
||
|
ds.slot := position
|
||
|
}
|
||
|
}
|
||
|
|
||
|
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||
|
|
||
|
function setContractOwner(address _newOwner) internal {
|
||
|
DiamondStorage storage ds = diamondStorage();
|
||
|
address previousOwner = ds.contractOwner;
|
||
|
ds.contractOwner = _newOwner;
|
||
|
emit OwnershipTransferred(previousOwner, _newOwner);
|
||
|
}
|
||
|
|
||
|
function contractOwner() internal view returns (address contractOwner_) {
|
||
|
contractOwner_ = diamondStorage().contractOwner;
|
||
|
}
|
||
|
|
||
|
function enforceIsContractOwner() internal view {
|
||
|
if(msg.sender != diamondStorage().contractOwner) {
|
||
|
revert NotContractOwner(msg.sender, diamondStorage().contractOwner);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
|
||
|
|
||
|
// Internal function version of diamondCut
|
||
|
function diamondCut(
|
||
|
IDiamondCut.FacetCut[] memory _diamondCut,
|
||
|
address _init,
|
||
|
bytes memory _calldata
|
||
|
) internal {
|
||
|
for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
|
||
|
bytes4[] memory functionSelectors = _diamondCut[facetIndex].functionSelectors;
|
||
|
address facetAddress = _diamondCut[facetIndex].facetAddress;
|
||
|
if(functionSelectors.length == 0) {
|
||
|
revert NoSelectorsProvidedForFacetForCut(facetAddress);
|
||
|
}
|
||
|
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
|
||
|
if (action == IDiamond.FacetCutAction.Add) {
|
||
|
addFunctions(facetAddress, functionSelectors);
|
||
|
} else if (action == IDiamond.FacetCutAction.Replace) {
|
||
|
replaceFunctions(facetAddress, functionSelectors);
|
||
|
} else if (action == IDiamond.FacetCutAction.Remove) {
|
||
|
removeFunctions(facetAddress, functionSelectors);
|
||
|
} else {
|
||
|
revert IncorrectFacetCutAction(uint8(action));
|
||
|
}
|
||
|
}
|
||
|
emit DiamondCut(_diamondCut, _init, _calldata);
|
||
|
initializeDiamondCut(_init, _calldata);
|
||
|
}
|
||
|
|
||
|
function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
|
||
|
if(_facetAddress == address(0)) {
|
||
|
revert CannotAddSelectorsToZeroAddress(_functionSelectors);
|
||
|
}
|
||
|
DiamondStorage storage ds = diamondStorage();
|
||
|
uint16 selectorCount = uint16(ds.selectors.length);
|
||
|
enforceHasContractCode(_facetAddress, "LibDiamondCut: Add facet has no code");
|
||
|
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
|
||
|
bytes4 selector = _functionSelectors[selectorIndex];
|
||
|
address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress;
|
||
|
if(oldFacetAddress != address(0)) {
|
||
|
revert CannotAddFunctionToDiamondThatAlreadyExists(selector);
|
||
|
}
|
||
|
ds.facetAddressAndSelectorPosition[selector] = FacetAddressAndSelectorPosition(_facetAddress, selectorCount);
|
||
|
ds.selectors.push(selector);
|
||
|
selectorCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
|
||
|
DiamondStorage storage ds = diamondStorage();
|
||
|
if(_facetAddress == address(0)) {
|
||
|
revert CannotReplaceFunctionsFromFacetWithZeroAddress(_functionSelectors);
|
||
|
}
|
||
|
enforceHasContractCode(_facetAddress, "LibDiamondCut: Replace facet has no code");
|
||
|
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
|
||
|
bytes4 selector = _functionSelectors[selectorIndex];
|
||
|
address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress;
|
||
|
// can't replace immutable functions -- functions defined directly in the diamond in this case
|
||
|
if(oldFacetAddress == address(this)) {
|
||
|
revert CannotReplaceImmutableFunction(selector);
|
||
|
}
|
||
|
if(oldFacetAddress == _facetAddress) {
|
||
|
revert CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(selector);
|
||
|
}
|
||
|
if(oldFacetAddress == address(0)) {
|
||
|
revert CannotReplaceFunctionThatDoesNotExists(selector);
|
||
|
}
|
||
|
// replace old facet address
|
||
|
ds.facetAddressAndSelectorPosition[selector].facetAddress = _facetAddress;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
|
||
|
DiamondStorage storage ds = diamondStorage();
|
||
|
uint256 selectorCount = ds.selectors.length;
|
||
|
if(_facetAddress != address(0)) {
|
||
|
revert RemoveFacetAddressMustBeZeroAddress(_facetAddress);
|
||
|
}
|
||
|
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
|
||
|
bytes4 selector = _functionSelectors[selectorIndex];
|
||
|
FacetAddressAndSelectorPosition memory oldFacetAddressAndSelectorPosition = ds.facetAddressAndSelectorPosition[selector];
|
||
|
if(oldFacetAddressAndSelectorPosition.facetAddress == address(0)) {
|
||
|
revert CannotRemoveFunctionThatDoesNotExist(selector);
|
||
|
}
|
||
|
|
||
|
|
||
|
// can't remove immutable functions -- functions defined directly in the diamond
|
||
|
if(oldFacetAddressAndSelectorPosition.facetAddress == address(this)) {
|
||
|
revert CannotRemoveImmutableFunction(selector);
|
||
|
}
|
||
|
// replace selector with last selector
|
||
|
selectorCount--;
|
||
|
if (oldFacetAddressAndSelectorPosition.selectorPosition != selectorCount) {
|
||
|
bytes4 lastSelector = ds.selectors[selectorCount];
|
||
|
ds.selectors[oldFacetAddressAndSelectorPosition.selectorPosition] = lastSelector;
|
||
|
ds.facetAddressAndSelectorPosition[lastSelector].selectorPosition = oldFacetAddressAndSelectorPosition.selectorPosition;
|
||
|
}
|
||
|
// delete last selector
|
||
|
ds.selectors.pop();
|
||
|
delete ds.facetAddressAndSelectorPosition[selector];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function initializeDiamondCut(address _init, bytes memory _calldata) internal {
|
||
|
if (_init == address(0)) {
|
||
|
return;
|
||
|
}
|
||
|
enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
|
||
|
(bool success, bytes memory error) = _init.delegatecall(_calldata);
|
||
|
if (!success) {
|
||
|
if (error.length > 0) {
|
||
|
// bubble up error
|
||
|
/// @solidity memory-safe-assembly
|
||
|
assembly {
|
||
|
let returndata_size := mload(error)
|
||
|
revert(add(32, error), returndata_size)
|
||
|
}
|
||
|
} else {
|
||
|
revert InitializationFunctionReverted(_init, _calldata);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
|
||
|
uint256 contractSize;
|
||
|
assembly {
|
||
|
contractSize := extcodesize(_contract)
|
||
|
}
|
||
|
if(contractSize == 0) {
|
||
|
revert NoBytecodeAtAddress(_contract, _errorMessage);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
// These facets are added to the diamond.
|
||
|
///////////////////////////////////////////////
|
||
|
|
||
|
contract DiamondCutFacet is IDiamondCut {
|
||
|
/// @notice Add/replace/remove any number of functions and optionally execute
|
||
|
/// a function with delegatecall
|
||
|
/// @param _diamondCut Contains the facet addresses and function selectors
|
||
|
/// @param _init The address of the contract or facet to execute _calldata
|
||
|
/// @param _calldata A function call, including function selector and arguments
|
||
|
/// _calldata is executed with delegatecall on _init
|
||
|
function diamondCut(
|
||
|
FacetCut[] calldata _diamondCut,
|
||
|
address _init,
|
||
|
bytes calldata _calldata
|
||
|
) external override {
|
||
|
LibDiamond.enforceIsContractOwner();
|
||
|
LibDiamond.diamondCut(_diamondCut, _init, _calldata);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The functions in DiamondLoupeFacet MUST be added to a diamond.
|
||
|
// The EIP-2535 Diamond standard requires these functions.
|
||
|
|
||
|
interface IERC165 {
|
||
|
/// @notice Query if a contract implements an interface
|
||
|
/// @param interfaceId The interface identifier, as specified in ERC-165
|
||
|
/// @dev Interface identification is specified in ERC-165. This function
|
||
|
/// uses less than 30,000 gas.
|
||
|
/// @return `true` if the contract implements `interfaceID` and
|
||
|
/// `interfaceID` is not 0xffffffff, `false` otherwise
|
||
|
function supportsInterface(bytes4 interfaceId) external view returns (bool);
|
||
|
}
|
||
|
|
||
|
// A loupe is a small magnifying glass used to look at diamonds.
|
||
|
// These functions look at diamonds
|
||
|
interface IDiamondLoupe {
|
||
|
/// These functions are expected to be called frequently
|
||
|
/// by tools.
|
||
|
|
||
|
struct Facet {
|
||
|
address facetAddress;
|
||
|
bytes4[] functionSelectors;
|
||
|
}
|
||
|
|
||
|
/// @notice Gets all facet addresses and their four byte function selectors.
|
||
|
/// @return facets_ Facet
|
||
|
function facets() external view returns (Facet[] memory facets_);
|
||
|
|
||
|
/// @notice Gets all the function selectors supported by a specific facet.
|
||
|
/// @param _facet The facet address.
|
||
|
/// @return facetFunctionSelectors_
|
||
|
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);
|
||
|
|
||
|
/// @notice Get all the facet addresses used by a diamond.
|
||
|
/// @return facetAddresses_
|
||
|
function facetAddresses() external view returns (address[] memory facetAddresses_);
|
||
|
|
||
|
/// @notice Gets the facet that supports the given selector.
|
||
|
/// @dev If facet is not found return address(0).
|
||
|
/// @param _functionSelector The function selector.
|
||
|
/// @return facetAddress_ The facet address.
|
||
|
function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
|
||
|
}
|
||
|
|
||
|
contract DiamondLoupeFacet is IDiamondLoupe, IERC165 {
|
||
|
// Diamond Loupe Functions
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
/// These functions are expected to be called frequently by tools.
|
||
|
//
|
||
|
// struct Facet {
|
||
|
// address facetAddress;
|
||
|
// bytes4[] functionSelectors;
|
||
|
// }
|
||
|
/// @notice Gets all facets and their selectors.
|
||
|
/// @return facets_ Facet
|
||
|
function facets() external override view returns (Facet[] memory facets_) {
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
uint256 selectorCount = ds.selectors.length;
|
||
|
// create an array set to the maximum size possible
|
||
|
facets_ = new Facet[](selectorCount);
|
||
|
// create an array for counting the number of selectors for each facet
|
||
|
uint16[] memory numFacetSelectors = new uint16[](selectorCount);
|
||
|
// total number of facets
|
||
|
uint256 numFacets;
|
||
|
// loop through function selectors
|
||
|
for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
|
||
|
bytes4 selector = ds.selectors[selectorIndex];
|
||
|
address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress;
|
||
|
bool continueLoop = false;
|
||
|
// find the functionSelectors array for selector and add selector to it
|
||
|
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
|
||
|
if (facets_[facetIndex].facetAddress == facetAddress_) {
|
||
|
facets_[facetIndex].functionSelectors[numFacetSelectors[facetIndex]] = selector;
|
||
|
numFacetSelectors[facetIndex]++;
|
||
|
continueLoop = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// if functionSelectors array exists for selector then continue loop
|
||
|
if (continueLoop) {
|
||
|
continueLoop = false;
|
||
|
continue;
|
||
|
}
|
||
|
// create a new functionSelectors array for selector
|
||
|
facets_[numFacets].facetAddress = facetAddress_;
|
||
|
facets_[numFacets].functionSelectors = new bytes4[](selectorCount);
|
||
|
facets_[numFacets].functionSelectors[0] = selector;
|
||
|
numFacetSelectors[numFacets] = 1;
|
||
|
numFacets++;
|
||
|
}
|
||
|
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
|
||
|
uint256 numSelectors = numFacetSelectors[facetIndex];
|
||
|
bytes4[] memory selectors = facets_[facetIndex].functionSelectors;
|
||
|
// setting the number of selectors
|
||
|
assembly {
|
||
|
mstore(selectors, numSelectors)
|
||
|
}
|
||
|
}
|
||
|
// setting the number of facets
|
||
|
assembly {
|
||
|
mstore(facets_, numFacets)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @notice Gets all the function selectors supported by a specific facet.
|
||
|
/// @param _facet The facet address.
|
||
|
/// @return _facetFunctionSelectors The selectors associated with a facet address.
|
||
|
function facetFunctionSelectors(address _facet) external override view returns (bytes4[] memory _facetFunctionSelectors) {
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
uint256 selectorCount = ds.selectors.length;
|
||
|
uint256 numSelectors;
|
||
|
_facetFunctionSelectors = new bytes4[](selectorCount);
|
||
|
// loop through function selectors
|
||
|
for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
|
||
|
bytes4 selector = ds.selectors[selectorIndex];
|
||
|
address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress;
|
||
|
if (_facet == facetAddress_) {
|
||
|
_facetFunctionSelectors[numSelectors] = selector;
|
||
|
numSelectors++;
|
||
|
}
|
||
|
}
|
||
|
// Set the number of selectors in the array
|
||
|
assembly {
|
||
|
mstore(_facetFunctionSelectors, numSelectors)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @notice Get all the facet addresses used by a diamond.
|
||
|
/// @return facetAddresses_
|
||
|
function facetAddresses() external override view returns (address[] memory facetAddresses_) {
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
uint256 selectorCount = ds.selectors.length;
|
||
|
// create an array set to the maximum size possible
|
||
|
facetAddresses_ = new address[](selectorCount);
|
||
|
uint256 numFacets;
|
||
|
// loop through function selectors
|
||
|
for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) {
|
||
|
bytes4 selector = ds.selectors[selectorIndex];
|
||
|
address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress;
|
||
|
bool continueLoop = false;
|
||
|
// see if we have collected the address already and break out of loop if we have
|
||
|
for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) {
|
||
|
if (facetAddress_ == facetAddresses_[facetIndex]) {
|
||
|
continueLoop = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
// continue loop if we already have the address
|
||
|
if (continueLoop) {
|
||
|
continueLoop = false;
|
||
|
continue;
|
||
|
}
|
||
|
// include address
|
||
|
facetAddresses_[numFacets] = facetAddress_;
|
||
|
numFacets++;
|
||
|
}
|
||
|
// Set the number of facet addresses in the array
|
||
|
assembly {
|
||
|
mstore(facetAddresses_, numFacets)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @notice Gets the facet address that supports the given selector.
|
||
|
/// @dev If facet is not found return address(0).
|
||
|
/// @param _functionSelector The function selector.
|
||
|
/// @return facetAddress_ The facet address.
|
||
|
function facetAddress(bytes4 _functionSelector) external override view returns (address facetAddress_) {
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
facetAddress_ = ds.facetAddressAndSelectorPosition[_functionSelector].facetAddress;
|
||
|
}
|
||
|
|
||
|
// This implements ERC-165.
|
||
|
function supportsInterface(bytes4 _interfaceId) external override view returns (bool) {
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
return ds.supportedInterfaces[_interfaceId];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// @title ERC-173 Contract Ownership Standard
|
||
|
/// Note: the ERC-165 identifier for this interface is 0x7f5828d0
|
||
|
/* is ERC165 */
|
||
|
interface IERC173 {
|
||
|
/// @dev This emits when ownership of a contract changes.
|
||
|
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||
|
|
||
|
/// @notice Get the address of the owner
|
||
|
/// @return owner_ The address of the owner.
|
||
|
function owner() external view returns (address owner_);
|
||
|
|
||
|
/// @notice Set the address of the new owner of the contract
|
||
|
/// @dev Set _newOwner to address(0) to renounce any ownership.
|
||
|
/// @param _newOwner The address of the new owner of the contract
|
||
|
function transferOwnership(address _newOwner) external;
|
||
|
}
|
||
|
|
||
|
contract OwnershipFacet is IERC173 {
|
||
|
function transferOwnership(address _newOwner) external override {
|
||
|
LibDiamond.enforceIsContractOwner();
|
||
|
LibDiamond.setContractOwner(_newOwner);
|
||
|
}
|
||
|
|
||
|
function owner() external override view returns (address owner_) {
|
||
|
owner_ = LibDiamond.contractOwner();
|
||
|
}
|
||
|
}
|
||
|
///////////////////////////////////////////////
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
// DiamondInit
|
||
|
// This contract and function are used to initialize state variables and/or do other actions
|
||
|
// when the `diamondCut` function is called.
|
||
|
// It is expected that this contract is customized if you want to deploy your diamond
|
||
|
// with data from a deployment script. Use the init function to initialize state variables
|
||
|
// of your diamond. Add parameters to the init funciton if you need to.
|
||
|
// DiamondInit can be used during deployment or for upgrades.
|
||
|
|
||
|
// Adding parameters to the `init` or other functions you add here can make a single deployed
|
||
|
// DiamondInit contract reusable accross upgrades, and can be used for multiple diamonds.
|
||
|
|
||
|
contract DiamondInit {
|
||
|
|
||
|
// You can add parameters to this function in order to pass in
|
||
|
// data to set your own state variables
|
||
|
function init() external {
|
||
|
// adding ERC165 data
|
||
|
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
|
||
|
ds.supportedInterfaces[type(IERC165).interfaceId] = true;
|
||
|
ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
|
||
|
ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
|
||
|
ds.supportedInterfaces[type(IERC173).interfaceId] = true;
|
||
|
|
||
|
// add your own state variables
|
||
|
// EIP-2535 specifies that the `diamondCut` function takes two optional
|
||
|
// arguments: address _init and bytes calldata _calldata
|
||
|
// These arguments are used to execute an arbitrary function using delegatecall
|
||
|
// in order to set state variables in the diamond during deployment or an upgrade
|
||
|
// More info in the EIP2535 Diamonds standard.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
// DiamondMultiInit
|
||
|
// This version of DiamondInit can be used to execute multiple initialization functions.
|
||
|
// It is expected that this contract is customized if you want to deploy or upgrade your diamond with it.
|
||
|
|
||
|
error AddressAndCalldataLengthDoNotMatch(uint256 _addressesLength, uint256 _calldataLength);
|
||
|
|
||
|
contract DiamondMultiInit {
|
||
|
|
||
|
// This function is provided in the third parameter of the `diamondCut` function.
|
||
|
// The `diamondCut` function executes this function to execute multiple initializer functions for a single upgrade.
|
||
|
|
||
|
function multiInit(address[] calldata _addresses, bytes[] calldata _calldata) external {
|
||
|
if(_addresses.length != _calldata.length) {
|
||
|
revert AddressAndCalldataLengthDoNotMatch(_addresses.length, _calldata.length);
|
||
|
}
|
||
|
for(uint i; i < _addresses.length; i++) {
|
||
|
LibDiamond.initializeDiamondCut(_addresses[i], _calldata[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////
|
||
|
// Diamond
|
||
|
// The diamond proxy contract.
|
||
|
|
||
|
|
||
|
// When no function exists for function called
|
||
|
error FunctionNotFound(bytes4 _functionSelector);
|
||
|
|
||
|
// This is used in diamond constructor
|
||
|
// more arguments are added to this struct
|
||
|
// this avoids stack too deep errors
|
||
|
struct DiamondArgs {
|
||
|
address owner;
|
||
|
address init;
|
||
|
bytes initCalldata;
|
||
|
}
|
||
|
|
||
|
contract Diamond {
|
||
|
|
||
|
// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
|
||
|
// The loupe functions are required by the EIP2535 Diamonds standard
|
||
|
|
||
|
constructor(IDiamondCut.FacetCut[] memory _diamondCut, DiamondArgs memory _args) payable {
|
||
|
LibDiamond.setContractOwner(_args.owner);
|
||
|
LibDiamond.diamondCut(_diamondCut, _args.init, _args.initCalldata);
|
||
|
|
||
|
// Code can be added here to perform actions and set state variables.
|
||
|
}
|
||
|
|
||
|
// Find facet for function that is called and execute the
|
||
|
// function if a facet is found and return any value.
|
||
|
fallback() external payable {
|
||
|
LibDiamond.DiamondStorage storage ds;
|
||
|
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
|
||
|
// get diamond storage
|
||
|
assembly {
|
||
|
ds.slot := position
|
||
|
}
|
||
|
// get facet from function selector
|
||
|
address facet = ds.facetAddressAndSelectorPosition[msg.sig].facetAddress;
|
||
|
if(facet == address(0)) {
|
||
|
revert FunctionNotFound(msg.sig);
|
||
|
}
|
||
|
// Execute external function from facet using delegatecall and return any value.
|
||
|
assembly {
|
||
|
// copy function selector and any arguments
|
||
|
calldatacopy(0, 0, calldatasize())
|
||
|
// execute function call using the facet
|
||
|
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
|
||
|
// get any return value
|
||
|
returndatacopy(0, 0, returndatasize())
|
||
|
// return any return value or error back to the caller
|
||
|
switch result
|
||
|
case 0 {
|
||
|
revert(0, returndatasize())
|
||
|
}
|
||
|
default {
|
||
|
return(0, returndatasize())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
receive() external payable {}
|
||
|
}
|