// SPDX-License-Identifier: FSL-1.1-MIT pragma solidity ^0.8.24; /** * \ * Author: Nick Mudge (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 * /***************************************************************************** */ // The functions in DiamondLoupeFacet MUST be added to a diamond. // The EIP-2535 Diamond standard requires these functions. import { LibDiamond } from "../libraries/LibDiamond.sol"; import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol"; import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol"; contract DiamondLoupeFacet is IDiamondLoupe, IERC165 { // Diamond Loupe Functions //////////////////////////////////////////////////////////////////// /// These functions are expected to be called frequently by tools. // // struct Facet { // address facetAddress; // bytes4[] functionSelectors; // } /// @notice Gets all facets and their selectors. /// @return facets_ Facet function facets() external view override returns (Facet[] memory facets_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; // create an array set to the maximum size possible facets_ = new Facet[](selectorCount); // create an array for counting the number of selectors for each facet uint8[] memory numFacetSelectors = new uint8[](selectorCount); // total number of facets uint256 numFacets; // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; bool continueLoop = false; // find the functionSelectors array for selector and add selector to it for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (facets_[facetIndex].facetAddress == facetAddress_) { facets_[facetIndex].functionSelectors[numFacetSelectors[facetIndex]] = selector; // probably will never have more than 256 functions from one facet contract require(numFacetSelectors[facetIndex] < 255, "amount of function has to be less than 255"); numFacetSelectors[facetIndex]++; continueLoop = true; break; } } // if functionSelectors array exists for selector then continue loop if (continueLoop) { continueLoop = false; continue; } // create a new functionSelectors array for selector facets_[numFacets].facetAddress = facetAddress_; facets_[numFacets].functionSelectors = new bytes4[](selectorCount); facets_[numFacets].functionSelectors[0] = selector; numFacetSelectors[numFacets] = 1; numFacets++; } for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { uint256 numSelectors = numFacetSelectors[facetIndex]; bytes4[] memory selectors = facets_[facetIndex].functionSelectors; // setting the number of selectors assembly { mstore(selectors, numSelectors) } } // setting the number of facets assembly { mstore(facets_, numFacets) } } /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return _facetFunctionSelectors The selectors associated with a facet address. function facetFunctionSelectors(address _facet) external view override returns (bytes4[] memory _facetFunctionSelectors) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; uint256 numSelectors; _facetFunctionSelectors = new bytes4[](selectorCount); // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; if (_facet == facetAddress_) { _facetFunctionSelectors[numSelectors] = selector; numSelectors++; } } // Set the number of selectors in the array assembly { mstore(_facetFunctionSelectors, numSelectors) } } /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view override returns (address[] memory facetAddresses_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; // create an array set to the maximum size possible facetAddresses_ = new address[](selectorCount); uint256 numFacets; // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; bool continueLoop = false; // see if we have collected the address already and break out of loop if we have for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (facetAddress_ == facetAddresses_[facetIndex]) { continueLoop = true; break; } } // continue loop if we already have the address if (continueLoop) { continueLoop = false; continue; } // include address facetAddresses_[numFacets] = facetAddress_; numFacets++; } // Set the number of facet addresses in the array assembly { mstore(facetAddresses_, numFacets) } } /// @notice Gets the facet address 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_ = ds.facetAddressAndSelectorPosition[_functionSelector].facetAddress; } // This implements ERC-165. function supportsInterface(bytes4 _interfaceId) external view override returns (bool) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); return ds.supportedInterfaces[_interfaceId]; } function getSelectors() external pure returns (bytes4[] memory) { bytes4[] memory selectors = new bytes4[](5); selectors[0] = DiamondLoupeFacet.facets.selector; selectors[1] = DiamondLoupeFacet.facetAddress.selector; selectors[2] = DiamondLoupeFacet.facetFunctionSelectors.selector; selectors[3] = DiamondLoupeFacet.facetAddresses.selector; selectors[4] = DiamondLoupeFacet.supportsInterface.selector; return selectors; } }