Summary of Ethereum Upgradeable Smart Contract R&D — Part 2 — 2020
This article is a summary of the new research and development since 2018 in the area of upgradeable Ethereum smart contracts. It presents the new works and standards that extend Summary of Ethereum Upgradeable Smart Contract R&D — Part 1–2018. Please read it to become familiar with the background leading up to this article.
This article intends to be a convenient and centralized place to understand upgradeable smart contract strategies. If you are watching or involved in this area of R&D, please comment on this article with any insights, feedback or comments that may be useful for others.
Most Ethereum projects are using upgradeable smart contracts strategies. The proxy pattern is by far the most adopted with projects like Gnosis, AragonOS, Melonport, Limechain, WindingTree, Terminal, Decentraland, and many others using this for fully or partially upgradeable contract systems.
The OpenZeppelin SDK upgradeable smart contract tools are a major enabler for this technology. Over 2,300+ downloads per week of @openzeppelin/upgrades and zos-lib and over 180,000 in total according to Github and growing. The OpenZeppelin SDK currently uses The transparent proxy pattern which is conforms with the EIP 1967 upgradable contract standard.
EIP 1538is the most adopted advanced upgradable contract standard, and current suggested by the EIP 1155 multi-token standard.
The use of separate logic and data contracts has had very limited adoption. Compound Finance uses this strategy for its partially upgradeable system.
New research and development has focused exclusively on the upgradeable proxy pattern. The works fall into two categories:
These provide a standard implementation of the proxy pattern. To a large extent, each of these standards solves the upgradable contract problem with slightly different underlying implementation. The main difference comes down to execution efficiency and the support for the standard by the community.
Upgradeable contracts using the proxy pattern loses transparency:
- the data structure and functions are less easily understood as they are defined in the logic contract, not the proxy themself.
- knowing when in a contract upgrade requires knowing the upgrade function signature
Implementation standards allow external viewers (e.g. block explorers) and other contracts to understand how to read and execute all of the functions in a contract and know when an upgrade occurs making the contract transparent again.
Since 2018, three implementation standards have been created:
- EIP 897: ERC DelegateProxy: created 2018–02–21
- EIP 1822: Universal Upgradeable Proxy Standard (UUPS): created 2019–03–04 this standard appears to be widely used. It is a subset of EIP 1967.
- EIP 1967: Standard Proxy Storage Slots: created 2019–04–24. OpenZeppelin SDK’s Transparent proxy pattern and the Unstructured Storage Proxies conform to this standard and have thus been widely used.
Extended Implementation and Structure Standards
These standards do the same as the implementation standards with extended functionality and standardization. These works solve problems in the simple proxy pattern as well as define standard naming conventions for data structures, upgrades, and events using the latest Ethereum EVM and compiler features.
Both the following standards contain multiple innovations that build on top of the proxy pattern, described below. The diamond standard builds on top of EIP 1358. Both works are being led by Nick Mudge.
EIP 1538: Transparent Contract Standard: created 2018–10–31
- Atomic multi-function upgrades — In previous standards, each proxy pointed to one logic contract which defined all the functions and data of the upgradeable contract. This meant that to upgrade one function you had to upgrade the entire logic contract. This standard allows multiple logic contracts for each proxy, with each function specifying what logic contract contains it uses. The consequence of this is that multiple functions can be added, removed and changed at once, by upgrading one of the logic contracts. This kind of function level upgradeability was first implemented and deployed on AragonOS.
- Delegate contract size limit — Ethereum contracts are limited to a maximum of 24kb. By allowing multiple logic contracts, this limitation is removed. Developers are able to compile the required business logic across multiple logic contracts. The proxy then is able to have a practically unlimited codebase of functions available to it, split in to several logic contracts.
- Standard function upgrade events — As this standard allows function level upgrades and atomic multi-function upgrades, it introduces event signatures to standardize upgrade monitoring.
- Non-upgradeable contract — This standard implements a standard way for the contract to be made non-upgradable. It does this by defining the upgrade function in the logic contract, making the upgrade function removable.
- Query functions — Provides a standardized way for contracts to query which functions exist and which logic contract they use.
Diamond Standard · Issue #2535: created 2020–03–02
- Atomic multi-function upgrades — Making use of the ABIencoderV2 (not experimental since Solidity v0.6.0), the upgrade function is able to take a dynamic list of the functions and logic contracts to upgrade and atomically upgrade them all at once in the same transaction. This removes the possibility of an inconsistent contract state. In EIP 1538 multiple functions could only be upgraded if they existed in the same logic contract.
- Storage structure flexibility — Making use of the new Solidity v0.6.4 language features, a logic contract only needs to inherit the state variables that it is actually going to use, not all state variables ever used by any logic contract and the proxy.
- Query function selectors — Instead of using function signatures, this standard uses function selectors for each upgradeable function to reduce gas costs and improve the ability for the contracts to be used/read correctly. This also makes use of the ABIencoderV2.
- Also adds some naming conventions such as “diamond” contract.
The upgradeable proxy pattern’s logic contract defines the variable layout which cannot change order. Each separate logic contract is responsible for this (usually by inheriting the storage contract) — this is the developer's responsibility. See this OpenZeppelin guide and this testing repository for more details. This has an impact on the OpenZeppelin SDK, and even more the Diamond Standard.
Upgrades to the Ethereum WASM, and Ethereum 2.0 architecture could cause breaking changes to proxy upgradeable contracts.
Currently deployed upgradable contracts are not able to change the upgradeability strategy.
For the above reasons, it is still suggested to make all functions pauseable so that a contract can be upgraded safely via a hard fork if upgradeability has an expected or unexpected problem.
Multiple upgradeable proxy standards have been created which have been adopted by the community. Standards allow for upgradable contracts to be transparent and more usable. EIP 1822 and 1967 have been widely adopted by the community with no reported security issues.
Advanced standards that improve and extend the functionality of the proxy pattern have been developed and are in use as well, using the latest Ethereum compiler and virtual machine. EIP 1538 has been adopted by the community. The diamond standard builds on top of EIP 1538, and is still in development before it becomes an EIP standard.
Using the latest standards makes upgradability, forward compatibility and issue mitigation easier for new contract deployments. Contracts that have already been deployed are not able to change their upgradability strategy. Developers using upgradeable strategies should feel comfortable with the mechanisms and limitations they provide.
Summary of Ethereum Upgradeable Smart Contract R&D — Part 2 — 2020 was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.