Skip to main content

DiamondLoupeFacet

Inspect diamond facets and their function selectors

Key Features
  • Exposes external functions for diamond introspection
  • Self-contained with no imports or inheritance
  • Compatible with ERC-2535 diamond standard

Overview

This facet provides introspection capabilities for a diamond, allowing developers to query facet addresses, function selectors, and facet mappings. It routes calls through the diamond proxy, enabling external inspection of the diamond's composition and upgradeability. Developers add this facet to understand and interact with a diamond's deployed facets.


Storage

FacetAndPosition

Definition
struct FacetAndPosition {
address facet;
uint32 position;
}

DiamondStorage

Definition
struct DiamondStorage {
mapping(bytes4 functionSelector => FacetAndPosition) facetAndPosition;
/**
* Array of all function selectors that can be called in the diamond.
*/
bytes4[] selectors;
}

Facet

Definition
struct Facet {
address facet;
bytes4[] functionSelectors;
}

State Variables

PropertyTypeDescription
DIAMOND_STORAGE_POSITIONbytes32Diamond storage slot position for this module (Value: keccak256("erc8109.diamond"))

Functions

facetFunctionSelectors

Gets all the function selectors supported by a specific facet. Returns the set of selectors that this diamond currently routes to the given facet address. How it works: 1. Iterates through the diamond’s global selector list (s.selectors) — i.e., the selectors that have been added to this diamond. 2. For each selector, reads its facet address from diamond storage (s.facetAndPosition[selector].facet) and compares it to _facet. 3. When it matches, writes the selector into a preallocated memory array and increments a running count. 4. After the scan, updates the logical length of the result array with assembly to the exact number of matches. Why this approach: - Single-pass O(n) scan over all selectors keeps the logic simple and predictable. - Preallocating to the maximum possible size (total selector count) avoids repeated reallocations while building the result. - Trimming the array length at the end yields an exactly sized return value.

function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetSelectors);

Parameters:

PropertyTypeDescription
_facetaddressThe facet address to filter by.

Returns:

PropertyTypeDescription
facetSelectorsbytes4[]The function selectors implemented by _facet.

facetAddresses

Get all the facet addresses used by a diamond. This function returns the unique set of facet addresses that provide functionality to the diamond. How it works:** 1. Uses a memory-based hash map to group facet addresses by the last byte of the address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store the unique facet addresses, avoiding an extra memory allocation for the intermediate array. The selectors array is overwritten with facet addresses as we iterate. 3. For each selector, looks up its facet address and checks if we've seen this address before by searching the appropriate hash map bucket. 4. If the facet is new (not found in the bucket), expands the bucket by 4 slots if it's full or empty, then adds the facet to both the bucket and the return array. 5. If the facet was already seen, skips it to maintain uniqueness. 6. Finally, sets the correct length of the return array to match the number of unique facets found. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly for each selector. - Growing in fixed-size chunks (4 for buckets) keeps reallocations infrequent and prevents over-allocation, while keeping bucket sizes small for sparse key distributions. - Reusing the selectors array memory eliminates one memory allocation and reduces total memory usage, which saves gas. - This design is optimized for diamonds with many selectors across many facets, where the original O(n²) nested loop approach becomes prohibitively expensive. - The 256-bucket hash map trades a small fixed memory cost for dramatic algorithmic improvement in worst-case scenarios.

function facetAddresses() external view returns (address[] memory allFacets);

Returns:

PropertyTypeDescription
allFacetsaddress[]Array of unique facet addresses used by this diamond.

facets

Gets all facets and their selectors. Returns each unique facet address currently used by the diamond and the list of function selectors that the diamond maps to that facet. How it works:** 1. Uses a memory-based hash map to group facets by the last byte of their address, reducing linear search costs from O(n²) to approximately O(n) for most cases. 2. Reuses the selectors array memory space to store pointers to Facet structs, avoiding an extra memory allocation for the intermediate array. 3. For each selector, looks up its facet address and checks if we've seen this facet before by searching the appropriate hash map bucket. 4. If the facet is new, expands the bucket by 4 slots if it's full or empty, creates a Facet struct with a 16-slot selector array, and stores a pointer to it in both the bucket and the facet pointers array. 5. If the facet exists, expands its selector array by 16 slots if full, then appends the selector to the array. 6. Finally, copies all Facet structs from their pointers into a properly-sized return array. Why this approach:** - Hash mapping by last address byte provides O(1) average-case bucket lookup instead of scanning all previously-found facets linearly. - Growing in fixed-size chunks (4 for buckets, 16 for selector arrays) keeps reallocations infrequent and prevents over-allocation. - Reusing the selectors array memory reduces total memory usage and allocation. - This design is optimized for diamonds with many facets and many selectors, where the original O(n²) nested loop approach becomes prohibitively expensive.

function facets() external view returns (Facet[] memory facetsAndSelectors);

Returns:

PropertyTypeDescription
facetsAndSelectorsFacet[]Array of Facet structs, each containing a facet address and function selectors.

Best Practices

Best Practice
  • Ensure this facet is added to the diamond during initialization.
  • Call facet inspection functions through the diamond proxy address.
  • Verify storage compatibility before upgrading facets to maintain introspection integrity.

Security Considerations

Security

Follow standard Solidity security practices. The getStorage function is marked pure and does not modify state. Inspection functions are view and do not pose reentrancy risks. Input validation is handled by the diamond proxy for external calls.

Was this helpful?
Last updated: