// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;//******************************************************************************\//* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)//* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535//*//* Implementation of a diamond.//******************************************************************************/import {LibDiamond} from"./libraries/LibDiamond.sol";
import {IDiamondCut} from"./interfaces/IDiamondCut.sol";
import {IDiamondLoupe} from"./interfaces/IDiamondLoupe.sol";
// When no function exists for function callederrorFunctionNotFound(bytes4 _functionSelector);
// This is used in diamond constructor// more arguments are added to this struct// this avoids stack too deep errorsstructDiamondArgs {
address owner;
address init;
bytes initCalldata;
}
contractDiamond{
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() 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;
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 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()) }
}
}
receive() externalpayable{}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;//******************************************************************************\//* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)//* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535//******************************************************************************/import {IDiamond} from"./IDiamond.sol";
interfaceIDiamondCutisIDiamond{
/// @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;
}
Contract Source Code
File 4 of 5: IDiamondLoupe.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;//******************************************************************************\//* 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{
/// These functions are expected to be called frequently/// by tools.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 5 of 5: LibDiamond.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;//******************************************************************************\//* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)//* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535//******************************************************************************/import {IDiamond} from"../interfaces/IDiamond.sol";
import {IDiamondCut} from"../interfaces/IDiamondCut.sol";
// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.// The loupe functions are required by the EIP2535 Diamonds standarderrorNoSelectorsGivenToAdd();
errorNotContractOwner(address _user, address _contractOwner);
errorNoSelectorsProvidedForFacetForCut(address _facetAddress);
errorCannotAddSelectorsToZeroAddress(bytes4[] _selectors);
errorNoBytecodeAtAddress(address _contractAddress, string _message);
errorIncorrectFacetCutAction(uint8 _action);
errorCannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector);
errorCannotReplaceFunctionsFromFacetWithZeroAddress(bytes4[] _selectors);
errorCannotReplaceImmutableFunction(bytes4 _selector);
errorCannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector);
errorCannotReplaceFunctionThatDoesNotExists(bytes4 _selector);
errorRemoveFacetAddressMustBeZeroAddress(address _facetAddress);
errorCannotRemoveFunctionThatDoesNotExist(bytes4 _selector);
errorCannotRemoveImmutableFunction(bytes4 _selector);
errorInitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);
libraryLibDiamond{
bytes32constant 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;
bool locked;
// 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;
}
functionenforceIsContractOwner() internalview{
if (msg.sender!= diamondStorage().contractOwner) {
revert NotContractOwner(msg.sender, diamondStorage().contractOwner);
}
}
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++) {
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);
} elseif (action == IDiamond.FacetCutAction.Replace) {
replaceFunctions(facetAddress, functionSelectors);
} elseif (action == IDiamond.FacetCutAction.Remove) {
removeFunctions(facetAddress, functionSelectors);
} else {
revert IncorrectFacetCutAction(uint8(action));
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
functionaddFunctions(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++;
}
}
functionreplaceFunctions(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 caseif (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;
}
}
functionremoveFunctions(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 diamondif (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];
}
}
functioninitializeDiamondCut(address _init, bytesmemory _calldata) internal{
if (_init ==address(0)) {
return;
}
enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
(bool success, bytesmemoryerror) = _init.delegatecall(_calldata);
if (!success) {
if (error.length>0) {
// bubble up error/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(error)
revert(add(32, error), returndata_size)
}
} else {
revert InitializationFunctionReverted(_init, _calldata);
}
}
}
functionenforceHasContractCode(address _contract, stringmemory _errorMessage) internalview{
uint256 contractSize;
assembly {
contractSize :=extcodesize(_contract)
}
if (contractSize ==0) {
revert NoBytecodeAtAddress(_contract, _errorMessage);
}
}
}