// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.8.18;/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/import { LibDiamond } from"./libraries/LibDiamond.sol";
import { IDiamondCut } from"./interfaces/IDiamondCut.sol";
contractDiamond{
receive() externalpayable{}
constructor(address _contractOwner, address _diamondCutFacet) payable{
LibDiamond.setContractOwner(_contractOwner);
// Add the diamondCut external function from the diamondCutFacet
IDiamondCut.FacetCut[] memory cut =new IDiamondCut.FacetCut[](1);
bytes4[] memory functionSelectors =newbytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamondCut.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});
LibDiamond.diamondCut(cut, address(0), "");
}
// Find facet for function that is called and execute the// function if a facet is found and return any value.fallback() externalpayable{
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storageassembly {
ds.slot:= position
}
// get facet from function selectoraddress facet = ds.facetAddressAndSelectorPosition[msg.sig].facetAddress;
require(facet !=address(0), "Diamond: Function does not exist");
// Execute external function from facet using delegatecall and return any value.assembly {
// copy function selector and any argumentscalldatacopy(0, 0, calldatasize())
// execute function call using the facetlet result :=delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return valuereturndatacopy(0, 0, returndatasize())
// return any return value or error back to the callerswitch result
case0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}
Contract Source Code
File 2 of 5: IDiamondCut.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.8.18;/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/interfaceIDiamondCut{
// Add=0, Replace=1, Remove=2enumFacetCutAction {
Add,
Replace,
Remove
}
structFacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @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 _initfunctiondiamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytescalldata _calldata
) external;
eventDiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}
Contract Source Code
File 3 of 5: IDiamondLoupe.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.8.18;/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/// A loupe is a small magnifying glass used to look at diamonds.// These functions look at diamondsinterfaceIDiamondLoupe{
structFacet {
address facetAddress;
bytes4[] functionSelectors;
}
/// @notice Gets all facet addresses and their four byte function selectors./// @return facets_ Facetfunctionfacets() externalviewreturns (Facet[] memory facets_);
/// @notice Gets all the function selectors supported by a specific facet./// @param _facet The facet address./// @return facetFunctionSelectors_functionfacetFunctionSelectors(address _facet) externalviewreturns (bytes4[] memory facetFunctionSelectors_);
/// @notice Get all the facet addresses used by a diamond./// @return facetAddresses_functionfacetAddresses() externalviewreturns (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.functionfacetAddress(bytes4 _functionSelector) externalviewreturns (address facetAddress_);
}
Contract Source Code
File 4 of 5: IERC165.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.8.18;interfaceIERC165{
/// @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` otherwisefunctionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 5 of 5: LibDiamond.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.8.18;/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/import { IDiamondCut } from"../interfaces/IDiamondCut.sol";
import { IDiamondLoupe } from"../interfaces/IDiamondLoupe.sol";
import { IERC165 } from"../interfaces/IERC165.sol";
libraryLibDiamond{
bytes32publicconstant DIAMOND_STORAGE_POSITION =keccak256("diamond.standard.diamond.storage");
structFacetAddressAndSelectorPosition {
address facetAddress;
uint16 selectorPosition;
}
structDiamondStorage {
// function selector => facet address and selector position in selectors arraymapping(bytes4=> FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition;
bytes4[] selectors;
mapping(bytes4=>bool) supportedInterfaces;
// owner of the contractaddress contractOwner;
}
functiondiamondStorage() internalpurereturns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot:= position
}
}
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
functionsetContractOwner(address _newOwner) internal{
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
functioncontractOwner() internalviewreturns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
functionenforceIsOwnerOrContract() internalview{
require(
msg.sender== diamondStorage().contractOwner ||msg.sender==address(this),
"LibDiamond: Must be contract or owner"
);
}
functionenforceIsContractOwner() internalview{
require(msg.sender== diamondStorage().contractOwner, "LibDiamond: Must be contract owner");
}
eventDiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
// Internal function version of diamondCutfunctiondiamondCut(
IDiamondCut.FacetCut[] memory _diamondCut,
address _init,
bytesmemory _calldata
) internal{
for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} elseif (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} elseif (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} else {
revert("LibDiamondCut: Incorrect FacetCutAction");
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
functionaddFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
require(_functionSelectors.length>0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
uint16 selectorCount =uint16(ds.selectors.length);
require(_facetAddress !=address(0), "LibDiamondCut: Add facet can't be address(0)");
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;
require(
oldFacetAddress ==address(0),
"LibDiamondCut: Can't add function that already exists"
);
ds.facetAddressAndSelectorPosition[selector] = FacetAddressAndSelectorPosition(
_facetAddress,
selectorCount
);
ds.selectors.push(selector);
selectorCount++;
}
}
functionreplaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
require(_functionSelectors.length>0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
require(_facetAddress !=address(0), "LibDiamondCut: Replace facet can't be address(0)");
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 diamondrequire(
oldFacetAddress !=address(this),
"LibDiamondCut: Can't replace immutable function"
);
require(
oldFacetAddress != _facetAddress,
"LibDiamondCut: Can't replace function with same function"
);
require(
oldFacetAddress !=address(0),
"LibDiamondCut: Can't replace function that doesn't exist"
);
// replace old facet address
ds.facetAddressAndSelectorPosition[selector].facetAddress = _facetAddress;
}
}
functionremoveFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
require(_functionSelectors.length>0, "LibDiamondCut: No selectors in facet to cut");
DiamondStorage storage ds = diamondStorage();
uint256 selectorCount = ds.selectors.length;
require(
_facetAddress ==address(0),
"LibDiamondCut: Remove facet address must be address(0)"
);
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
bytes4 selector = _functionSelectors[selectorIndex];
FacetAddressAndSelectorPosition memory oldFacetAddressAndSelectorPosition = ds
.facetAddressAndSelectorPosition[selector];
require(
oldFacetAddressAndSelectorPosition.facetAddress !=address(0),
"LibDiamondCut: Can't remove function that doesn't exist"
);
// can't remove immutable functions -- functions defined directly in the diamondrequire(
oldFacetAddressAndSelectorPosition.facetAddress !=address(this),
"LibDiamondCut: Can't remove immutable function."
);
// 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];
}
}
functioninitializeDiamondCut(address _init, bytesmemory _calldata) internal{
if (_init ==address(0)) {
require(
_calldata.length==0,
"LibDiamondCut: _init is address(0) but_calldata is not empty"
);
} else {
require(
_calldata.length>0,
"LibDiamondCut: _calldata is empty but _init is not address(0)"
);
if (_init !=address(this)) {
enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
}
(bool success, bytesmemoryerror) = _init.delegatecall(_calldata);
if (!success) {
if (error.length>0) {
// bubble up the errorrevert(string(error));
} else {
revert("LibDiamondCut: _init function reverted");
}
}
}
}
functionenforceHasContractCode(address _contract, stringmemory _errorMessage) internalview{
uint256 contractSize;
assembly {
contractSize :=extcodesize(_contract)
}
require(contractSize >0, _errorMessage);
}
}