// 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; for (uint256 i; i < numFacets; ) { uint256 functionSelectorsLength = facets_[i] .functionSelectors .length; for (uint256 j; j < functionSelectorsLength; ) { bool kIncrementor; for (uint256 k; k < defaultFacet.length; ) { bool oIncrementor; for ( uint256 o; o < defaultFacet[k].functionSelectors.length; ) { if ( facets_[i].functionSelectors[j] == defaultFacet[k].functionSelectors[o] ) { if (defaultFacet[k].functionSelectors.length == 1) { defaultFacet = removeFacetElement( defaultFacet, k ); kIncrementor = true; break; } defaultFacet[k].functionSelectors = removeElement( defaultFacet[k].functionSelectors, o ); oIncrementor = true; } if (!oIncrementor) { unchecked { ++o; } } oIncrementor = false; } if (!kIncrementor) { unchecked { ++k; } } kIncrementor = false; } unchecked { ++j; } } unchecked { ++i; } } 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; } } jIncrementor = false; } } else { __facets[i] = defaultFacet[defaultFacetIndex]; ++defaultFacetIndex; } if (!iIncrementor) { unchecked { ++i; } } 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; } }
0.4.18