DEV Community

Cover image for Understanding Diamonds on Ethereum
Nick Mudge
Nick Mudge

Posted on • Edited on

Understanding Diamonds on Ethereum

The EIP-2535 Diamonds is a way to organize your Solidity code and contracts to give them the right amount of modularity and cohesion for your system. In addition, flexible upgrade capability is a key part of it.

Let's get defined what a diamond actually is. A diamond is a contract that uses the external functions of other contracts as its own.

The contracts that a diamond uses for its external functions are called facets.

A diamond has a mapping inside of it that associates functions to contracts (facets) that have those functions. When an external function is called on a diamond the diamond looks in the mapping to find the facet to retrieve the function from and execute.

Of course, a diamond is implemented according to EIP-2535 Diamonds. That way tools can be used with them and it is easier to understand and work with people's contracts because they follow the standard.

Let's look at a diagram that shows a simple diamond. For simplicity generic names of things are used in this diagram. You should use names that make sense for your system.

Diamond Diagram

In the diagram above you can see that functions func1 and func2 are associated with FacetA. Functions func3, func4, func5 are associated with FacetB. Functions func6 and func7 are associated with FacetC.

Also in this diagram you see that different structs within the diamond are used by different facets. FacetA uses DiamondStorage3. FacetB uses DiamondStorage3 and DiamondStorage2. FacetC uses DiamondStorage2 and DiamondStorage1.

The diagram above shows the DiamondStorage structs in the diamond. It is true that all contract storage data is stored in the diamond, not in its facets. But the struct definitions exist in the facet source code. Or the struct definitions can exist in separate solidity files or Solidity libraries that are imported by facets, or the struct definitions can be in a contract that is inherited by multiple facets.

A key part of the Diamond Standard is how it helps you with contract storage. It introduces a new storage technique called Diamond Storage. It is a new technique because until recently it wasn't possible to do.

By default, when you create new state variables like unsigned integers, structs, mappings etc. Solidity automatically takes care of where exactly these things are stored within contract storage. But this default automatic functionality becomes a problem when upgrading diamonds with new facets. New facets declaring new state variables clobber existing state variables -- data for new state variables gets written to where existing state variables exist.

Diamond Storage solves this problem by bypassing Solidity's automatic storage location mechanism by enabling you to specify where your data gets stored within contract storage.

This might sound risky but it is not if you use a hash of a string that applies to your application or is specific to your application. Use the hash as the starting location of where to store your data in contract storage.

Doing that might seem risky to you too. But it is not. Realize that this is how Solidity's storage location mechanism works for maps and arrays. Solidity uses hashes of data for starting locations of data stored in contract storage. You can do it too.

Modularity by Decoupling Facets from Each Other

In the past it was common for a facet to contain within its source code every single state variable that was ever used by the diamond, in the order they were first declared. Or at least these facets contained state variables in their source code that they didn't use. This was done to avoid the problem of a new facet clobbering existing state variables.

This problem is now solved with Diamond Storage. With Diamond Storage the source code of a facet can just contain the state variables that it actually needs and there is no concern about overwriting existing state variables.

This means that facets that use Diamond Storage are independent from each other and it means that these facets can be reused by different diamonds. In this way facets become reusable libraries for diamonds.

diamondCut, the Swiss-Army Knife for Contract Upgrades

The diamondCut function enables you to add, replace and remove any number of functions from a diamond in a single transaction. For example in one function call to diamondCut you can add 3 new functions, replace 6 functions and remove 4 functions. Or you could call diamondCut one time to add one function. This is really flexible.

In addition the diamondCut function emits an event that shows all changes made to a diamond. It records all additions, replacements and removals of functions.

The Loupe

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

The Diamond Standard provides 4 functions that are used to provide information about what functions and facets are currently stored or used in a diamond. Together these functions are called 'the loupe'. Any diamond implements these functions. See the Diamond Standard for more information about them.

How to Get Started Making Your Diamond

  1. The most important thing is reading and understanding the Diamond Standard. If something is unclear let me know!

  2. The second important thing is using a Diamond Standard reference implementation.

The reference implementation is more than a reference implementation. It is the boilerplate code you need for a diamond. Also, using the reference implementation makes your diamond compliant with the standard.

Calling Diamond Functions

In order to call a function that exists in a diamond you need to use the ABI information of the facet that has the function.

Here is an example that uses web3.js:

let myUsefulFacet = new web3.eth.Contract(
  MyUsefulFacet.abi, 
  diamondAddress
)
Enter fullscreen mode Exit fullscreen mode

In the code above we create a contract variable so we can call contract functions with it.

In this example we know we will use a diamond because we pass a diamond's address as the second argument. But we are using an ABI from the MyUsefulFacet facet so we can call functions that are defined in that facet. MyUsefulFacet's functions must have been added to the diamond (using diamondCut) in order for the diamond to use the function information provided by the ABI of course.

Similarly you need to use the ABI of a facet in Solidity code in order to call functions from a diamond. Here's an example of Solidity code that calls a function from a diamond:

string result = MyUsefulFacet(diamondAddress).getResult()
Enter fullscreen mode Exit fullscreen mode

Get Help and Join the Community

If you need help or would like to discuss diamonds then send me a message on twitter, or email me. Or join the Diamond Standard Discord server.

Top comments (3)

Collapse
 
gavinzheng profile image
Gavin Zheng • Edited

Great article !!! I am working on developing NFT artifacts by using Diamond standard. I still am unclear about several points in Diamond standard:

  1. Based on my understanding, the mapping key is function selector. What if different facets have the same function signature? For example : FacetA has a function setfacet() and FacetB has a function with the same function prototype like setfacet(). I think it will cause name conflict.
  2. How contract storage can be upgraded? If I define all the contract storage in Diamond contract, and if I need to add a new state variable when I need to upgrade some facet , what I should do?
  3. is there any design addressing how to access older contract code and storage? Is there any plan to add version control to manage contract code and storage

Thanks in advance

Collapse
 
mudgen profile image
Nick Mudge

Great questions Gavin.

The diamondCut function which adds/replaces/removes functions prevents the same function selectors from being added.

Contract storage can be upgraded by adding state variables to the ends of structs. Also more areas of storage can be accessed using diamond storage. Info on that here: dev.to/mudgen/how-diamond-storage-...

I use git to manage contract source code. I use git tags to label deployments and upgrades to help keep track of things. It would be great if more tools and/or practices were published about this.

Collapse
 
jomarip profile image
Jomari Peterson

I do like the modularity aspect. I would love to see an implementation of this integrated with IPFS around modular NFTs. Have you seen the work from Async.art . I think this flows well with their vision