// SPDX-License-Identifier: MIT
pragma solidity ^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 "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { LibDiamond } from "../storage/LibDiamond.sol";
import { IDiamond } from "../interfaces/IDiamond.sol";
import { IDiamondCut } from "../interfaces/IDiamondCut.sol";
import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol";
import "../storage/RoleAccessControl.sol";
// When no function exists for function called
error FunctionNotFound(bytes4 _functionSelector);
contract Diamond {
constructor(address _diamondCutFacet, address _diamondLoupeFacet, address _init, address admin) payable {
RoleAccessControl.grantRole(admin, RoleAccessControl.ROLE_ADMIN);
IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](2);
bytes4[] memory functionSelectors = new bytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamond.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamond.FacetCutAction.Add,
functionSelectors: functionSelectors
});
bytes4[] memory loupeFunctionSelectors = new bytes4[](4);
loupeFunctionSelectors[0] = IDiamondLoupe.facets.selector;
loupeFunctionSelectors[1] = IDiamondLoupe.facetAddresses.selector;
loupeFunctionSelectors[2] = IDiamondLoupe.facetAddress.selector;
loupeFunctionSelectors[3] = IDiamondLoupe.facetFunctionSelectors.selector;
cut[1] = IDiamond.FacetCut({
facetAddress: _diamondLoupeFacet,
action: IDiamond.FacetCutAction.Add,
functionSelectors: loupeFunctionSelectors
});
LibDiamond.diamondCut(cut, _init, abi.encodeWithSignature("init()"));
}
// 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 = LibDiamond.diamondStorage();
// 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 {}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^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";
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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^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 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_);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
/******************************************************************************\
* 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 standard
error NoSelectorsGivenToAdd();
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("xyz.elfi.diamond.storage");
struct FacetAddressAndSelectorPosition {
address facetAddress;
uint16 selectorPosition;
}
struct DiamondStorage {
/// @notice Maps function selectors to facet addresses and their positions in the selectors array
mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition;
/// @notice Array of function selectors
bytes4[] selectors;
/// @notice Maps function selectors to supported interfaces
mapping(bytes4 => bool) supportedInterfaces;
}
/// @notice Returns the diamond storage
/// @return ds The diamond storage
function diamondStorage() internal pure returns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot := position
}
}
/// @notice Emitted when a diamond cut is executed
/// @param _diamondCut The diamond cut array
/// @param _init The address of the initialization contract
/// @param _calldata The calldata for the initialization function
event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
/// @notice Internal function to execute a diamond cut
/// @param _diamondCut The diamond cut array
/// @param _init The address of the initialization contract
/// @param _calldata The calldata for the initialization function
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);
}
/// @notice Adds functions to the diamond
/// @param _facetAddress The address of the facet
/// @param _functionSelectors The function selectors to add
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++;
}
}
/// @notice Replaces functions in the diamond
/// @param _facetAddress The address of the facet
/// @param _functionSelectors The function selectors to replace
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;
}
}
/// @notice Removes functions from the diamond
/// @param _facetAddress The address of the facet
/// @param _functionSelectors The function selectors to remove
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];
}
}
/// @notice Initializes the diamond cut
/// @param _init The address of the initialization contract
/// @param _calldata The calldata for the initialization function
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);
}
}
}
/// @notice Ensures that the contract has code
/// @param _contract The address of the contract
/// @param _errorMessage The error message to revert with if the contract has no code
function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
uint256 contractSize;
assembly {
contractSize := extcodesize(_contract)
}
if (contractSize == 0) {
revert NoBytecodeAtAddress(_contract, _errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/// @title RoleAccessControl
/// @dev Library for managing role-based access control.
library RoleAccessControl {
using EnumerableSet for EnumerableSet.Bytes32Set;
bytes32 internal constant ACCESS_CONTROL_KEY = keccak256(abi.encode("xyz.elfi.storage.AccessControl"));
bytes32 constant ROLE_ADMIN = "ADMIN";
bytes32 constant ROLE_UPGRADE = "UPGRADE";
bytes32 constant ROLE_CONFIG = "CONFIG";
bytes32 constant ROLE_KEEPER = "KEEPER";
/// @dev Error thrown when an account does not have the required role.
error InvalidRoleAccess(address account, bytes32 role);
/// @dev Error thrown when an invalid role name is provided.
error InvalidRoleName(bytes32 role);
struct Props {
mapping(address => EnumerableSet.Bytes32Set) accountRoles;
}
/// @dev Loads the role access control storage.
/// @return self The role access control storage.
function load() public pure returns (Props storage self) {
bytes32 s = ACCESS_CONTROL_KEY;
assembly {
self.slot := s
}
}
/// @dev Checks if the caller has the specified role.
/// @param role The role to check.
function checkRole(bytes32 role) internal view {
if (!hasRole(msg.sender, role)) {
revert InvalidRoleAccess(msg.sender, role);
}
}
/// @dev Checks if the caller has the specified role.
/// @param role The role to check.
/// @return True if the caller has the role, false otherwise.
function hasRole(bytes32 role) internal view returns (bool) {
return hasRole(msg.sender, role);
}
/// @dev Checks if an account has the specified role.
/// @param account The account to check.
/// @param role The role to check.
/// @return True if the account has the role, false otherwise.
function hasRole(address account, bytes32 role) internal view returns (bool) {
Props storage self = load();
return self.accountRoles[account].contains(role);
}
/// @dev Grants a role to an account.
/// @param account The account to grant the role to.
/// @param role The role to grant.
function grantRole(address account, bytes32 role) internal {
Props storage self = load();
self.accountRoles[account].add(role);
}
/// @dev Revokes a role from an account.
/// @param account The account to revoke the role from.
/// @param role The role to revoke.
function revokeRole(address account, bytes32 role) internal {
Props storage self = load();
if (self.accountRoles[account].contains(role)) {
self.accountRoles[account].remove(role);
}
}
/// @dev Revokes all roles from an account.
/// @param account The account to revoke all roles from.
function revokeAllRole(address account) internal {
Props storage self = load();
uint256 length = self.accountRoles[account].length();
for (uint256 i; i < length; i++) {
bytes32 value = self.accountRoles[account].at(0);
self.accountRoles[account].remove(value);
}
}
}
{
"compilationTarget": {
"contracts/router/Diamond.sol": "Diamond"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 10
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_diamondCutFacet","type":"address"},{"internalType":"address","name":"_diamondLoupeFacet","type":"address"},{"internalType":"address","name":"_init","type":"address"},{"internalType":"address","name":"admin","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotAddFunctionToDiamondThatAlreadyExists","type":"error"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"CannotAddSelectorsToZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotRemoveFunctionThatDoesNotExist","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotRemoveImmutableFunction","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceFunctionThatDoesNotExists","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet","type":"error"},{"inputs":[{"internalType":"bytes4[]","name":"_selectors","type":"bytes4[]"}],"name":"CannotReplaceFunctionsFromFacetWithZeroAddress","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_selector","type":"bytes4"}],"name":"CannotReplaceImmutableFunction","type":"error"},{"inputs":[{"internalType":"bytes4","name":"_functionSelector","type":"bytes4"}],"name":"FunctionNotFound","type":"error"},{"inputs":[{"internalType":"uint8","name":"_action","type":"uint8"}],"name":"IncorrectFacetCutAction","type":"error"},{"inputs":[{"internalType":"address","name":"_initializationContractAddress","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"InitializationFunctionReverted","type":"error"},{"inputs":[{"internalType":"address","name":"_contractAddress","type":"address"},{"internalType":"string","name":"_message","type":"string"}],"name":"NoBytecodeAtAddress","type":"error"},{"inputs":[{"internalType":"address","name":"_facetAddress","type":"address"}],"name":"NoSelectorsProvidedForFacetForCut","type":"error"},{"inputs":[{"internalType":"address","name":"_facetAddress","type":"address"}],"name":"RemoveFacetAddressMustBeZeroAddress","type":"error"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]