Diamond Loupe Functions
Tags: smartcontracts, solidity, ethereum, blockchain
Published: 2020-10-08 20:34:36

> A loupe is a magnifying glass that is used to look at diamonds.

> In [EIP-2535 Diamond Standard](https://eips.ethereum.org/EIPS/eip-2535) a loupe is four standard read-only functions that tell you what functions and facets are provided by a diamond.

> A facet is a smart contract that supplies diamonds with external functions.

If you look at the source code of a diamond it will show a fallback function. It doesn't show what external functions it has. To get that information you need to call one or more of the four standard, read-only loupe functions. 

Here are the four standard read-only loupe functions:

* `facetAddresses()` Returns all the facet addresses used by a diamond.
* `facetAddress(bytes4 _functionSelector)` Returns the facet address that implements a functions.
* `facetFunctionSelectors(address _facet)` Returns all the functions used by a diamond that come from a particular facet.
* `facets()` Returns an array consisting of all the facet addresses and functions used by a diamond.

The loupe functions can be used for many useful things including:
1. Use the facet addresses to query services like Etherscan to retrieve and show all the source code used by a diamond.
1. Retrieve ABI info for a diamond.
1. Tools and programming libraries use them to deploy diamonds.
1. Tools and programming libraries use them to upgrade diamonds.
1. User interfaces use them to show information about diamonds.
1. User interfaces use them to enable users to show and call functions on diamonds.
1. Tests use them to verify that correct functions and facets are being used correctly.

The loupe functions make diamonds transparent. This is important for understanding, showing and using exactly the functionality provided by diamonds. 

They are important for security to verify that diamonds are used correctly and not mistakenly or maliciously.

They are important for testing to ensure that diamonds contain the correct functions and facets and to test code that uses diamonds.

## Another Way. Transparency With Events

The loupe functions are not the only way to find out what functions and facets a diamond has.

Anytime a function/facet is added/replaced/removed on a diamond the `DiamondCut` event is emitted that records exactly what changed in a diamond.

All the `DiamondCut` events for a diamond can be queried and an algorithm executed to determine what facets and functions currently exists for a diamond.

You might wonder why we have two ways to do the same thing.

First, the `DiamondCut` event provides something that the loupe functions don't: A historical record of all upgrades done on a diamond. The historical record can be used to verify the state, correctness and security of a diamond over time.

So you might wonder why have loupe functions when events can be used to get the current functions and facets used by a diamond.

## Why Have Loupe Functions When You can Use Events?

To be clear, this isn't a debate of events vs external functions. The `DiamondCut` events are a given and there is no restriction on their use.  The debate is this: **Why have events *and* loupe functions?**

Let's look at the tradeoffs of having/not having loupe functions.

**Pros**

1. **More choice** People that want to use event systems, such as the [The Graph](https://thegraph.com/), or event database systems, to determine the contents of diamonds can do so. People who want to call the loupe external functions on diamonds to get the same data, can do so. Having events and the loupe functions make diamonds easier to use for more people because they can choose how to get the data.

1. **Easier adoption by services and tools** Services such as Etherscan, blockchain explorers, web applications, programming libraries and other software may prefer or require the use of events or external functions. Having both options increases the capability to integrate with  tools and services.

1. **More transparent, reliable and available** If an event system is not available or up-to-date or is slow or has any other problem, then the loupe functions can be used. If external functions can't be called in a particular context then an event system or database can be used. Having both methods available increases the reliability, availability and transparency of diamonds.

1. **Simple and easy** The loupe functions are simple and easy to use. They are regular external contract functions that are called with regular client contract libraries like web3.js or ethers.js. No special custom software or third party software or library or dependency is needed to call the loupe functions, since they are regular contract functions. <br>
Keeping data, code and logic within the diamond loupe functions simplifies client software that needs the data.<br>
Software that uses events has to rely on custom software or a third-party library to query all the `DiamondCut` events and execute logic to determine the current functions and facets.

1. **Secure** The code for the `diamondCut` function and the diamond loupe functions is carefully looked at by Solidity developers and security experts. These functions are simple enough for a competent Solidity developer to read and understand.

1. **In theory the loupe functions can be executed on-chain.** In theory the loup functions in the [diamond-3](https://github.com/mudgen/diamond-3) implementation are gas-efficient enough to be called and used in transactions. 

**Cons**

**Loupe functions add code to a diamond.** 

However a DiamondLoupeFacet only needs to be deployed once and can be reused by many diamonds.

The [diamond-1](https://github.com/mudgen/diamond-1) implementation does the following to implement loupe functions:
  1. Stores an array of function selectors. When a new function is added its selector is added to the function selectors array. When a function is removed its selector is removed from the function selectors array.
  1. The loupe functions are implemented like this:
      1. `facetAddress(bytes4 _functionSelector)` The facet address that is associated with the `_functionSelector` is found and returned.
      1. `facetAddresses` The function selectors are looped through to find all the facet addresses used by the diamond. An array of facet addresses is returned.
      1. `facetFunctionSelectors(address _facet)` The function selectors are looped through to find all the selectors that belong to the facet specified by `_facet`. An array of selectors is returned.
      1. `facets()` The function selectors are looped through to find all the facet addresses and function selectors used by the diamond. These are returned.

The [diamond-2](https://github.com/mudgen/diamond-2) implementation is implemented the same way as diamond-1 except that its code is more complicated because it is gas-optimized.

The [diamond-3](https://github.com/mudgen/diamond-3) implementation does the following to implement loupe functions:
  1. Stores an array of facet addresses. For each facet address an array of function selectors is stored. When a new facet is added it is added to the array of facet addresses. When a facet is removed it is removed from the array of facet addresses. When a function is added its selector is added to an array of function selectors. When a function is removed its selector is removed from an array of function selectors.

1. The loupe functions are very simple and implemented like this:
      1. `facetAddress(bytes4 _functionSelector)` The facet address that is associated with the `_functionSelector` is found and returned.
      1. `facetAddresses` The array of facet addresses is returned.
      1. `facetFunctionSelectors(address _facet)` The array of function selectors for the facet is returned.
      1. `facets()` The array of facet addresses is looped through to get each facet address and its function selectors. These are returned.

Note that the loupe functions are read-only and are isolated in their own facet. They do not affect other facets of a diamond. Because of this the loupe functions do not increase complexity of the main application code or other facets.

**Loupe functions require more gas for adding/replacing/removing functions** Loupe functions require that some data be stored in the diamond so that function selectors and facet addresses can be retrieved. The [diamond-2](https://github.com/mudgen/diamond-2) implementation is the most efficient for adding/replacing/removing functions. It stores 8 function selectors in a single 32-byte contract storage slot and avoids storage reads and writes.

I don't have gas metrics for diamond implementations that don't have loupe functions.  But I do have some gas metrics for diamond implementations that do have loupe functions. 

1. **diamond-1 implementation** To add 20 functions from a single facet costs 617,348 gas.
2. **diamond-2 implementation** To add 20 functions from a single facet costs 536,246 gas.
3. **diamond-3 implementation** To add 20 functions from a single facet costs 683,000 gas.

## Overall

I think having events *and* loupe functions is worth it. I've been working on [EIP-2535 Diamonds](https://eips.ethereum.org/EIPS/eip-2535) and diamond implementations to be useful for myself and you and others. So I am interested in your input about this.