// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.18; import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; import {IERC165} from "../../interfaces/ERC/IERC165.sol"; import {IERC1271} from "../../interfaces/ERC/IERC1271.sol"; import {IERC677Receiver} from "../../interfaces/ERC/IERC677Receiver.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {LibDiamond} from "../../libraries/LibDiamond.sol"; import {LibLoupe} from "../../libraries/LibLoupe.sol"; import {IDiamondCut} from "../../facets/base/interfaces/IDiamondCut.sol"; import {IStorageLoupe} from "./interfaces/IStorageLoupe.sol"; import {IDiamondLoupe} from "./interfaces/IDiamondLoupe.sol"; /** * @title DiamondLoupe Facet * @dev DiamondLoupe contract compatible with EIP-2535 * @author David Yongjun Kim (@Powerstream3604) */ contract DiamondLoupeFacet is IDiamondLoupe, IStorageLoupe, IERC165 { // Diamond Loupe Functions //////////////////////////////////////////////////////////////////// /// These functions are expected to be called frequently by tools off-chain. /// @notice Gets all facets and their selectors. /// @return __facets Facet function facets() public view override returns (Facet[] memory __facets) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); Facet[] memory defaultFacet = IDiamondLoupe(ds.defaultFallbackHandler) .facets(); Facet[] memory facets_ = LibLoupe.facets(); uint256 numFacets = facets_.length; bytes4[] memory keys; address[] memory values; for (uint256 i; i < numFacets; i++) { for (uint256 j; j < facets_[i].functionSelectors.length; j++) { (keys, values) = setValue(keys, values, facets_[i].functionSelectors[j], facets_[i].facetAddress); } } { bool iIncrement; for (uint256 i; i < defaultFacet.length; ) { bool jIncrement; for (uint256 j; j < defaultFacet[i].functionSelectors.length; ) { if (getValue(keys, values, defaultFacet[i].functionSelectors[j]) != address(0)) { if (defaultFacet[i].functionSelectors.length == 1) { defaultFacet = removeFacetElement( defaultFacet, i ); iIncrement = true; break; } defaultFacet[i].functionSelectors = removeElement( defaultFacet[i].functionSelectors, j ); jIncrement = true; } if (!jIncrement) { unchecked { ++j; } } else { jIncrement = false; } } if(!iIncrement) { unchecked { ++i; } } else { iIncrement = false; } } } { uint256 facetLength = numFacets + defaultFacet.length; __facets = new Facet[](facetLength); uint256 defaultFacetIndex; bool iIncrementor; for (uint256 i; i < facetLength; ) { if (i < numFacets) { __facets[i] = facets_[i]; bool jIncrementor; for (uint256 j; j < defaultFacet.length; ) { if ( __facets[i].facetAddress == defaultFacet[j].facetAddress ) { __facets[i].functionSelectors = mergeArrays( facets_[i].functionSelectors, defaultFacet[j].functionSelectors ); defaultFacet = removeFacetElement(defaultFacet, j); jIncrementor = true; { __facets = removeFacetElement( __facets, __facets.length - 1 ); } --facetLength; iIncrementor = true; } if (!jIncrementor) { unchecked { ++j; } } else { jIncrementor = false; } } } else { __facets[i] = defaultFacet[defaultFacetIndex]; ++defaultFacetIndex; } if (!iIncrementor) { unchecked { ++i; } } else { iIncrementor = false; } } } } /// @notice Gets all the function selectors provided by a facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors( address _facet ) external view override returns (bytes4[] memory facetFunctionSelectors_) { Facet[] memory facet = facets(); uint256 facetLength = facet.length; for (uint256 i; i < facetLength; ) { if (facet[i].facetAddress == _facet) return facet[i].functionSelectors; unchecked { ++i; } } return facetFunctionSelectors_; } /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view override returns (address[] memory facetAddresses_) { Facet[] memory facet = facets(); uint256 facetLength = facet.length; facetAddresses_ = new address[](facetLength); for (uint256 i; i < facetLength; ) { facetAddresses_[i] = facet[i].facetAddress; unchecked { ++i; } } } /// @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 override returns (address facetAddress_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); facetAddress_ = address(bytes20(ds.facets[_functionSelector])); if (facetAddress_ == address(0)) { facetAddress_ = IDiamondLoupe(ds.defaultFallbackHandler) .facetAddress(_functionSelector); } } function supportsInterface( bytes4 _interfaceId ) external view override returns (bool) { return _interfaceId == type(IERC165).interfaceId || _interfaceId == IDiamondCut.diamondCut.selector || _interfaceId == type(IDiamondLoupe).interfaceId || _interfaceId == type(IERC1155Receiver).interfaceId || _interfaceId == type(IERC721Receiver).interfaceId || _interfaceId == type(IERC777Recipient).interfaceId || _interfaceId == IERC1271.isValidSignature.selector || _interfaceId == type(IERC677Receiver).interfaceId || LibDiamond.diamondStorage().supportedInterfaces[_interfaceId]; } function facetsFromStorage() external view override returns (Facet[] memory) { return LibLoupe.facets(); } function facetAddressFromStorage( bytes4 _functionSelector ) external view override returns (address) { return LibLoupe.facetAddress(_functionSelector); } function facetAddressesFromStorage() external view override returns (address[] memory) { return LibLoupe.facetAddresses(); } function facetFunctionSelectorsFromStorage( address _facet ) external view override returns (bytes4[] memory) { return LibLoupe.facetFunctionSelectors(_facet); } // Internal utility functions function mergeArrays( bytes4[] memory array1, bytes4[] memory array2 ) internal pure returns (bytes4[] memory) { uint256 length1 = array1.length; uint256 length2 = array2.length; bytes4[] memory mergedArray = new bytes4[](length1 + length2); for (uint256 i; i < length1; ) { mergedArray[i] = array1[i]; unchecked { ++i; } } for (uint256 i; i < length2; ) { mergedArray[length1 + i] = array2[i]; unchecked { ++i; } } return mergedArray; } function removeFacetElement( Facet[] memory facets_, uint256 index ) internal pure returns (Facet[] memory) { require(index < facets_.length, "Invalid index"); require(facets_.length != 0, "Invalid array"); // Create a new array with a length of `facets_.length - 1` Facet[] memory newArray = new Facet[](facets_.length - 1); uint256 newArrayLength = newArray.length; // Iterate over the original array, skipping the element at the specified `index` for (uint256 i; i < newArrayLength; ) { if (i < index) { newArray[i] = facets_[i]; } else { newArray[i] = facets_[i + 1]; } unchecked { ++i; } } return newArray; } function removeElement( bytes4[] memory array, uint256 index ) internal pure returns (bytes4[] memory) { require(index < array.length, "Invalid index"); require(array.length != 0, "Invalid array"); bytes4[] memory newArray = new bytes4[](array.length - 1); uint256 newArrayLength = newArray.length; for (uint256 i; i < newArrayLength; ) { if (i < index) { newArray[i] = array[i]; } else { newArray[i] = array[i + 1]; } unchecked { ++i; } } return newArray; } function setValue( bytes4[] memory keys, address[] memory values, bytes4 key, address value ) public pure returns (bytes4[] memory, address[] memory) { uint256 index = findIndex(keys, key); if (index < keys.length) { values[index] = value; } else { // Create new storage arrays bytes4[] memory newKeys = new bytes4[](keys.length + 1); address[] memory newValues = new address[](values.length + 1); // Copy values to the new storage arrays for (uint256 i = 0; i < keys.length; i++) { newKeys[i] = keys[i]; newValues[i] = values[i]; } // Add the new key-value pair newKeys[keys.length] = key; newValues[values.length] = value; return (newKeys, newValues); } // If the key already exists, return the original arrays return (keys, values); } function getValue(bytes4[] memory keys, address[] memory values, bytes4 key) public pure returns (address) { uint256 index = findIndex(keys, key); if (index >= keys.length) return address(0); return values[index]; } function findIndex(bytes4[] memory keys, bytes4 key) internal pure returns (uint256) { for (uint256 i = 0; i < keys.length; i++) { if (keys[i] == key) { return i; } } return keys.length; } }
0.4.18