Dry Goods: Best Security Practices for Diamond Agency Contracts

Proxy contracts are an important tool for smart contract developers. Today, there are many proxy modes and corresponding usage rules in the contract system. We have previously outlined upgradable proxy contract security best practices.

In this article, we will introduce another proxy model that is popular in the developer community, the diamond proxy model.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Diamond proxy contracts, also known as "diamonds", are a design pattern for Ethereum smart contracts introduced by Ethereum Improvement Proposal (EIP) 2535.

Diamond mode allows a contract to have unlimited functionality by splitting its functionality into smaller contracts (also aptly called "aspects"). Diamond acts as a proxy, routing function calls to the appropriate aspect.

The design of the diamond model can solve the problem of the maximum contract size limitation of the Ethereum network. By breaking down a large contract into smaller aspects, the diamond pattern allows developers to build more complex and feature-rich smart contracts without being affected by size constraints.

Diamond Brokerage offers tremendous flexibility compared to traditional upgradable contracts. They allow contract parts to be upgraded, adding, replacing or removing selected parts of functions without touching other parts.

This article provides an overview of EIP-2535, including a comparison with the widely used transparent proxy mode and UUPS proxy mode, and its security considerations for the developer community.

Dry Goods: Best Security Practices for Diamond Agency Contracts

In the context of EIP-2535, a "diamond" is a proxy contract whose functional implementation is provided by different logical contracts, called "aspects".

Imagine that a real diamond has different sides, called facets, and the corresponding Ethereum diamond contracts also have different facets. Each contract of diamond borrowing function is a different side or facet.

The diamond standard uses an analogy to extend the capabilities of the "diamond cut" to add, replace, or delete facets and features.

In addition, the Diamond Standard provides a feature called "Diamond Loupe" that returns information about the facets and presence of the diamond.

Compared with the traditional proxy model, the "diamond" is equivalent to the proxy contract, and the different "aspects" correspond to the realization of the contract. Different aspects of a diamond agent can share internal functions, libraries, and state variables. The key components of a diamond are as follows:

Dry Goods: Best Security Practices for Diamond Agency Contracts

A central contract that acts as a proxy, routing function calls to the appropriate aspect. It contains a mapping of function selectors to "aspect" addresses.

Dry Goods: Best Security Practices for Diamond Agency Contracts

A single contract that implements a specific function. Each facet contains a set of functions that can be called by the diamond.

Dry Goods: Best Security Practices for Diamond Agency Contracts

is a set of standard functions defined in EIP-2535 that provide information about facet and function selectors used in diamonds. Diamond Loupe allows developers and users to inspect and understand the structure of diamonds.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Functions for adding, replacing, or removing facets in a diamond and their corresponding feature selectors. Only authorized addresses (e.g., the owner of the diamond or a multi-signature contract) can perform diamond cutting.

Similar to traditional agents, when a function call is made on a diamond agent, the agent's fallback function (fallback function) is triggered. The main difference from the diamond proxy is that in the fallback function, there is a selectorToFacet mapping, which stores and determines which logical contract address has the implementation of the called function. It then uses a delegatecall to execute the function, just like a traditional proxy.

Dry Goods: Best Security Practices for Diamond Agency Contracts

All proxies use the fallback() function to delegate function calls to external addresses. Below is the implementation of the diamond proxy and the implementation of the traditional proxy.

It is worth noting that their assembly code blocks are very similar, so the only difference is the aspect address in the diamond proxy delegate call and the impl address in the traditional proxy delegate call.

The main difference is: in the diamond proxy, the address of the aspect is determined by the hashmap from the caller's msg.sig (function selector) to the address of the aspect, while in the traditional proxy, the impl address does not depend on the caller's enter.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Diamond agent fallback function

Dry Goods: Best Security Practices for Diamond Agency Contracts

Traditional proxy fallback function

Dry Goods: Best Security Practices for Diamond Agency Contracts

The SelectorToFacet mapping determines which contract contains the implementation of each function selector. Project workers often need to add, replace, or remove this function selector-to-implementation contract mapping. EIP-2535 states: In order to achieve this, there must be a diamondCut() function. Below is an example interface.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Each FacetCut structure contains a facet address and a four-byte feature selector array to be updated in the diamond proxy contract. FaceCutAction allows one to add, replace and remove feature selectors. The implementation of the diamondCut() function should include adequate access control, prevent slot collisions, recover on failure, etc.

Dry Goods: Best Security Practices for Diamond Agency Contracts

In order to query what functions and facets a diamond agent has, we use the "diamond magnifying glass". "Diamond Loupe" is a special aspect that implements the following interface defined in EIP-2535:

Dry Goods: Best Security Practices for Diamond Agency Contracts

The facets() function should return the addresses of all facets and their four-byte function selectors. The facetFunctionSelectors() function should return all function selectors supported by a particular aspect. The facetAddresses() function should return all facet addresses used by a diamond.

The facetAddress() function should return an aspect that supports the given selector, or address( 0 ) if not found. Note that there should not be more than one aspect address with the same feature selector.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Given that diamond proxies delegate different function calls to different implementing contracts, it is critical to manage storage slots properly to prevent conflicts. EIP-2535 mentions several storage slot management methods.

Dry Goods: Best Security Practices for Diamond Agency Contracts

This aspect can declare state variables in the structure. This aspect can use any number of structures, each with a different storage location. Each structure has a specific location in contract storage. Aspects can declare their own state variables, but they cannot conflict with the storage locations of state variables declared by other aspects. A sample library and diamond storage contract are provided in EIP-2535, as shown in the following figure:

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

App Store is a more specialized version of Diamond Store. This pattern is used to more conveniently and easily share state variables of aspects. An App store structure is defined to contain any number and type of state variables required by an application. An aspect always declares the AppStorage structure as the first and only state variable, at position 0 of the storage slot. Different aspects can then access variables from this structure.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

There are also other storage slot management strategies, including a hybrid of Diamond Storage and AppStorage. For example, some structures are shared between different aspects, and some are specific to a specific aspect. In all cases, it is very important to prevent accidental slot collisions.

Comparison with transparent proxy and UUPS proxy

The two main proxy modes currently used by the Web3 developer community are the transparent proxy mode and the UUPS proxy mode. In this section, we briefly compare the diamond proxy mode with the transparent proxy and UUPS proxy modes.

Dry Goods: Best Security Practices for Diamond Agency Contracts

1.EPI-2535: #Facets,% 20 State% 20 iables% 20 and% 20 Diamond% 20 Storage

2.EPI-1967:

3.Diamond proxy reference implementation:

4.OpenZeppelin implementation:

Dry Goods: Best Security Practices for Diamond Agency Contracts

Agents and scalable solutions are more complex systems, and OpenZeppelin provides code libraries and comprehensive documentation for UUPS, Transparent and Beacon scalable agents. However for the diamond proxy pattern, although OpenZeppelin affirmed its benefits, they still decided not to include the EIP-2535 diamond implementation in their library.

Therefore, developers using existing third-party libraries or implementing this solution themselves must implement it with extreme care. Here we have compiled a checklist of security best practices for the developer community to consider.

Dry Goods: Best Security Practices for Diamond Agency Contracts

By breaking down contract logic into smaller, more manageable modules, developers can more easily test and audit their code.

Additionally, this approach allows developers to focus on specific aspects of building and maintaining contracts, rather than managing a complex, monolithic code base. The end result is a more flexible and modular code base that can easily be updated and modified without affecting other parts of the contract.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Source: Aavegotchi Github

Dry Goods: Best Security Practices for Diamond Agency Contracts

When the diamond proxy contract is deployed, it must add the address of the DiamondCutFacet contract to the diamond proxy contract and implement the diamondCut() function. The diamondCut() function is used to add, delete or replace facets and functions, without DiamondCutFacet and diamondCut(), the diamond agent cannot work properly.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

Source: Mugen's Diamond-3-Hardhat

Dry Goods: Best Security Practices for Diamond Agency Contracts

When adding a new state variable to a storage structure in a smart contract, it must be added at the end of the structure. Adding a new state variable at the beginning or in the middle of a structure will cause the new state variable to overwrite existing state variable data, and any state variable after the new state variable may refer to the wrong memory location.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

The AppStorage pattern requires that one and only one structure be declared for the diamond proxy, and that this structure be shared by all aspects. If multiple structures are required, the DiamondStorage pattern should be used.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

Don't put a struct directly inside another struct unless you're sure you don't intend to add more state variables to the inner struct. It is not possible to add new state variables to internal structs in an upgrade without overwriting variable storage slots declared after the struct.

Dry Goods: Best Security Practices for Diamond Agency Contracts

The workaround is to add new state variables to the memory-mapped struct instead of placing the "struct" directly in the "struct". Variable storage slots in a map are computed differently and are not contiguous in storage.

Dry Goods: Best Security Practices for Diamond Agency Contracts

The size of the array will be affected by the size of the structure. When a new state variable is added to a structure, it changes the size and layout of that structure.

This can cause problems if the structure is used as an element in an array. If the size and layout of the structure changes, then the size and layout of the array will also change, which can cause problems with indexing or other operations that rely on consistent size and layout of the structure.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

Similar to other proxy patterns, each variable should have a unique storage slot. Otherwise, two different structures at the same location would overwrite each other.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

The initialize() function is usually used to set important variables, such as addresses of privileged roles. If it is not initialized when the contract is deployed, a malicious actor can call and control the contract.

It is recommended to add appropriate access control to the initialization/setting function, or ensure that the function is called when the contract is deployed and cannot be called again.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Dry Goods: Best Security Practices for Diamond Agency Contracts

If any aspect of the contract is able to call the selfdestruct() function, it has the potential to destroy the entire contract, resulting in loss of funds or data. This is extremely dangerous in the diamond proxy mode, as multiple aspects can access the storage and data of the proxy contract.

Dry Goods: Best Security Practices for Diamond Agency Contracts

Currently, we see more and more projects adopting the diamond proxy model in their smart contracts. It offers flexibility and other advantages over traditional proxies.

However, the extra flexibility can also mean a wider attack surface for attackers. We hope this article helps the developer community understand the mechanics of the diamond proxy model and its security considerations.

At the same time, the project team should conduct rigorous testing and third-party audits to reduce the risk of vulnerabilities related to the implementation of diamond agency contracts.

CertiK will continue to publish such technical articles to help more developers develop securely. Follow us to get more similar information and information!

View Original
The content is for reference only, not a solicitation or offer. No investment, tax, or legal advice provided. See Disclaimer for more risks disclosure.
  • Reward
  • Comment
  • Share
Comment
0/400
No comments