Writing Upgradable Smart Contracts

Phyzixmusic
Hexmount
Published in
6 min readJul 21, 2023
Photo by Milad Fakurian on Unsplash

Writing smart contracts today has changed a lot since the initial EVM development boom a few years ago. Back then, everyone was cloning different ICO contracts and creating pretty much standard ERC20 clones again and again with little to no customisation to their contracts. Fast forwards a few years, new standards and patterns were born, mainly lead by open source libraries like OpenZepplin, these patterns have become the de-facto tools every developer needs to kick start their smart contract development and satisfy clients needs.

If you are a developer, you can automatically relate to the issue where client's requirements are constantly changing, especially for clients that are not familiar with the immutability of the blockchain, it becomes challenging if not impossible to foresee project requirements before deployment and actually testing it in the market. In this article, we will be identifying & addressing some of the patterns that are available to mitigate this problem, we will focus on the theoretical aspect of the approaches and keep code to a minimum. In a future article series, we will explore the approaches in deep technical detail.

Eternal Storage Pattern

The Eternal Storage pattern is an approach for designing upgradeable smart contracts. The term “Eternal Storage” itself refers to the persistence layer of the smart contract, where data is stored indefinitely and can be accessed by successive versions of the contract logic.

The fundamental idea behind the Eternal Storage pattern is the separation of data and logic. In this design, the data is stored in a contract that is separate from the contract containing the business logic. This separation enables the logic contract to be replaced or upgraded without risking data loss or corruption, thereby providing a way to alter the smart contract after deployment without disturbing the stored data.

The data contract in an Eternal Storage pattern acts as a sort of ‘database’ for the DApp, with access control measures in place to ensure that only authorized contracts can interact with the data. This data contract uses a flexible storage structure, often a mapping of keys (representing variable names) to their values, to allow for arbitrary data to be stored.

Key Features of the Eternal Storage Pattern

  • Easy to understand
  • No Assembly
  • Works without libraries
  • Eliminates the storage migration

Limitations of the Eternal Storage Pattern

  • Address of contracts changes (might require updating other systems to point to it)
  • Difficult access pattern for variables
  • Doesn't work out of the box

Proxy Contracts

The first proxy contracts first appeared around 2016 and since then there has been a lot of improvements

What is a delegate call?

A delegate call is a mechanism that takes the logic of a smart contract call and executes it in the scope of another target contract

What are storage collisions?

Storage collisions occur when a delegate call is made, and the storage layout of the calling contract does not match that of the called contract. This mismatch can lead to data being written to the wrong storage slots, causing unintended and often disastrous results. To prevent this, it’s important to ensure that the storage layout of all interacting contracts aligns perfectly.

Storage collisions were a common problem with early implementations of proxy contracts. However, the introduction of standards like EIP-1822 and EIP-1967 has largely mitigated this risk by defining specific storage slots for contract logic, reducing the likelihood of storage collisions.

EIP-897: ERC Delegate Proxy

Initially created in 2018, it mainly addresses the use of delegate calls in a standardized way without the issue of any storage collisions. This is done by having our logic/target contract inherit from a storage contract

A delegate call, in essence, is a type of message call in Ethereum that executes the called contract’s code within the context of the calling contract, maintaining the state of the calling contract. This allows for flexible control and reuse of contract code, making it a powerful tool for upgradeable contracts.

The primary purpose of ERC Delegate Proxy is to standardize the interface for two types of proxy contracts, which are:

  1. Stateful delegate proxies: These have an owner or an admin who can change the implementation of the contract and upgrade it to a new version.
  2. Stateless delegate proxies: These delegate every call to a fixed implementation contract that can never be changed.

This standard helps distinguish between these two types of proxies and enables other contracts to make informed decisions on how to interact with the proxy.

This standard also outlines an event, ProxyImplementationUpdated, which is emitted whenever the implementation contract is changed. This provides a public, transparent record of all updates, allowing users and developers to verify the contract's history.

The ERC Delegate Proxy standard has laid the groundwork for many of the upgradeability patterns we see today. Its focus on standardizing the way contracts handle delegate calls has made the process of developing upgradeable contracts more straightforward and less error-prone. By providing a solid foundation and a common interface for proxy contracts, EIP-897 has become a critical piece of the puzzle in the development of upgradeable smart contracts.

EIP-1822: UUPS

Created in 2019, building on top of the efforts made in EIP-897, it defines another way of avoiding storage collisions without having the need to inherit proxy storage. it stores the contract logic on a specific storage slot and does not let solidity choose the first storage slot

Notes: it becomes hard to identify the target contract especially on etherscan where users are unable to identify the address of the target contract where the logic lives.

EIP-1967: Standard Storage Slots

EIP-1967 proposes a standard for deterministic storage slots for proxies. The standard aims to mitigate the risk of storage collisions by defining specific storage slots for certain pieces of data. These slots are derived using the keccak256 hash function, ensuring they’re unlikely to collide with storage slots chosen by Solidity.

Implementing EIP-1967 ensures that the location of the logic contract and the admin address are stored in specific, consistent locations across all proxy contracts, making it easier for developers and users to interact with the contracts.

Key Features:

  1. Predictability: The use of deterministic storage slots makes it easy to locate important data, like the admin address and implementation contract.
  2. Security: By reducing the risk of storage collisions, EIP-1967 improves the overall safety of proxy contracts.

Limitations:

  1. Rigid Structure: The defined storage slots can limit flexibility. Deviating from the standard may cause incompatibility issues with other contracts expecting the standard structure

EIP-2523: Dimond Standard

EIP-2535, known as the Diamond Standard, presents a strategy for making upgradeable contracts by allowing multiple contracts to share a single contract’s storage, essentially combining the eternal storage and proxy contract patterns.

The Diamond Standard allows you to create a contract architecture where different facets of a contract can be upgraded or replaced without affecting the other facets or the contract’s overall state. This is achieved by organizing the contract into smaller, more manageable parts that can be independently upgraded.

Key Features:

  1. Modularity: The Diamond Standard encourages a modular contract structure, making code easier to manage and reason about.
  2. Upgradability: With the ability to upgrade or replace individual facets, developers have granular control over contract upgrades.

Limitations:

  1. Complexity: Implementing the Diamond Standard can be complex due to the management of multiple facets and function selectors.
  2. Gas Costs: Increased gas costs may result from the use of multiple delegate calls.

Metamorphosis Contracts

Metamorphosis contracts are an evolution of the Diamond standard, introducing additional flexibility and simplicity to the concept of upgradeable contracts. Metamorphosis contracts not only allow individual functions to be replaced or upgraded, but they also support the addition of completely new functionalities over time.

This allows for a contract to essentially ‘morph’ over time, adapting to new requirements or improvements as they become necessary. This level of granular control over contract upgrades makes Metamorphosis contracts an ideal solution for complex, evolving blockchain systems.

Key Features:

  1. Versatility: Metamorphosis contracts support the replacement, upgrade, and addition of functionalities, allowing for versatile contract design.
  2. Adaptability: With the ability to ‘morph’ over time, these contracts can adapt to changing requirements or improvements.

Limitations:

  1. Complexity: The added flexibility of Metamorphosis contracts increases their complexity, requiring a high level of expertise to implement correctly.
  2. Testing Challenges: Due to their changeable nature, thorough testing of Metamorphosis contracts can be more difficult and time-consuming.

Key Takeaways and Future Outlook

Upgradeable smart contracts have seen significant advancements over the years, evolving from basic ERC20 clones to complex systems involving proxy contracts, eternal storage, and advanced standards like the Diamond and Metamorphosis contracts. These systems offer increased flexibility and control, allowing blockchain developers to continually improve and adapt their contracts to changing needs and requirements.

As we move forward, the industry will likely see even more advancements and standards aimed at making upgradeable contracts simpler, safer, and more efficient. As a developer in the blockchain space, understanding these techniques and being able to implement them effectively is an essential skill.

This turned out to be a rather long post. Appreciate your attention!

--

--