Proxy — Diamonds

Eszymi
Coinmonks
Published in
5 min readOct 26, 2022

--

In my previous post, I wrote about two mainly used proxy pattern: Transparent Proxy Pattern and UUPS (Universal Upgradeable Proxy Standard). As I said there, this pattern was created to avoid proxy selector clashing. The Diamonds, also known as Multi-Facet Proxy, solves this problem too. But except that, it solves one more problem.

New problem — size of the smart contract

The maximal size of the smart contract is 24 kB, and nothing more. Of course, it’s a problem if we would like to create more complex and elaborate contract. And here the Diamonds comes into. It’s solution of this problem is easy and sounds “let’s split this contract to more contracts”. Easy, isn’t it? If we use 2 contracts we could write 48 kB sized file, if 3 contracts 72 kB etc. Because there is no limitation of number of connected contracts, we are able to create so complex and long file as we wish. So, when we know how to extend the size of contract over the limit, we should focus on the way how to connect all of them, and make this connection as smooth as it is possible.

Diamond and its facets

As I said in the title of this post, Diamonds is a proxy. So it’s a clue how it could work. In this pattern we have the main contract — diamond and all contracts we would like to use — facets. An example of the system build as Diamonds is show on this picture. In the rest of this post, I will try to explain all elements showed here.

Diamond with facets. The picture comes from here.

In diamond is implemented fallback function that give us access to use external functions from facets. All facets are separate, independent contracts and library. Facets could share internal functions and state variables with other facets. Moreover, all facets are stateless contract — it’s mean they don’t have contract storage. The big advantage of using facets is possibility to use even previously deployed contract as facet and use one facet by many different diamonds.

The facets are able to read and write from the storage of the diamond. Diamond could use as a storage, an eternal storage pattern. The advantage of this way to manage of the storage is the option to use as many variables as we need. This pattern of storage is not the only one, that could be used.

How to find the called function through all facets?

If our contract is just one contract, the calling of one from implemented their function is an easy task. If our contract is simply proxy with proxy and logic layer, this task is more difficult. We have to have address of current address of logic’s contract and then use delegatcall with this address and with signature of called function. But how should we make it, when our functions are implemented not in one, but in plenty of contracts?

When I asked myself of that, my first shot was using of mapping. And as I read after, it was a good shot. In the Diamonds we use a mapping where the key is the selector of the function, and the value corresponding to it is the address of the smart contract where this function is implemented. We see this on the picture in the yellow area labeled as Data.

When someone would like to use any function from facets, he called to the diamond contract, and this use fallback function. There is implemented delegatcall function, that connects with the contract which correspond in the mapping to the selector of chosen function.

Because the keys in mapping are independent, different element, we won’t have two values connected with one key — the selector. Thanks to that, we avoid the proxy selector clashing problem, between different facets.

Adding, replacing and removing functions from diamond

The strength of the Diamonds patterns is possibility to relatively easy change list of used by it functions. The heart of this pattern is mapping contained selectors of the functions and the addresses of facets. So adding, replacing or removing a function from diamond is just making this operation on the correct element from the mapping. But if everyone would be able to make it, there will be a huge mess, and huuuge gaps of security. Therefor, there is defined function to these operations — diamondCut. This function updates any number of functions from any number of facets in a single transaction. So we see it is atomically operation, in other words it’s all-or-nothing operation. Executing all changes within a single transaction prevents data corruption, which could occur in upgrades done over multiple transactions. Thanks defined function to making any change in the core mapping of our diamond, we protect ourselves.

Diamonds pattern doesn’t have defined standard of the diamond ownership/authentication. It is possibility to create any arbitrary mechanism to grant permission to use diamondCut. The pattern doesn’t limit use in this matter, but we should be very careful with the grant of access to this function.

Pros and cons

The benefits of this pattern are:

  • one, constant address of diamond
  • possibility to use function from a lot of various contracts
  • easy way to add/remove or replace function used by diamond
  • access to create contract bigger than 24 kB
  • reusing of the previously deployed contracts
  • separate functionality can be implemented in separate facets and used together in a diamond

The biggest disadvantage of this pattern is the complexity of it. Why do I think that? Because when the system is more sophisticate, there is more place for security lacks. In this pattern, we use a lot of different function from different contracts. This system is more demanding than simpler proxy pattern in the term of maintains and checking of the security.

I hope you find this post useful. If you have any idea, how could I make my posts better, let my know. I am always ready to learn. You can connect with me on LinkedIn and Telegram.

If you would like to talk with me about this or any other topic I wrote, feel free. I’m open to conversation.

Happy learning!

New to trading? Try crypto trading bots or copy trading

--

--