100% Upgradeable Mechanisms

Creating a smart contract that can be completely replaced with new logic is possible just by using more smart contract infrastructure. There are two main streams of strategies: proxies and the separation of logic and data into different contracts. Separating logic and data in a contract can be done in two ways.

The fundamental problem that both methods solve is how to update a contract’s logic while still retaining access to a contract’s state.

I have given a brief summary of how these strategies work below but I recommend going to the latest resources at the bottom of this article to become intimately familiar with how they work (especially if you are just starting down the rabbit hole of upgradeable contracts).

Proxy Contracts

A proxy contract uses the delegatecall opcode to forward function calls to a target contract which can be updated. As delegatecall retains the state of the function call, the target contract’s logic can be updated and the state will remain in the proxy contract for the updated target contract’s logic to use. As with delegatecall, the msg.sender will remain that of the caller of the proxy contract.

Due to the recent Byzantine hard fork, which now gives access to the return size of a function call, this method can now be generic (compared to when it was first proposed by Nick Johnson). An example of a generic proxy contract can be seen in Daonomic’s resources which is also is a great article to read about this mechanism in more detail to get a simple overview.

Update June 2018: There are several ways to create an upgradeable contract using a proxy with delegate call. Zeppelin has a very good articles explaining the three patterns that they have researched and tested. The pattern that has been chosen for the ZeppelinOS smart contract system is called the Unstructured Storage pattern and has now gone through a full security audit by Nomic Labs. This audit identified one critical issue with the pattern which has now been fixed.

Separate Logic and Data Contracts

This involves separating the smart contract into a data contract which contains the data (variables, structures, mappings etc) with appropriate getters and setters, and a logic contract which contains all of the business logic of how to update this data. The logic contract updates the data through the setters and the data contract only allows the logic contract to call the setters. This allows the logic to be replaced while keeping the data in the same place, allowing for a fully upgradeable system.

The contract can be updated by pointing users to use the new logic contract (through a resolver such as ENS) and updating the data contract permissions to allow the new logic contract to be able to execute the setters.

Check out @Thomas Wiesner’s video to get a better understanding of this mechanism.

Separate Logic and Data Contracts with Data as Key-Value pairs

This strategy works with a similar principle to the one above, except that instead of using the final desired data structures that your contract would normally use (structs, mappings etc), all data is abstracted down and stored in primitive key-value pairs. A standard naming system along with the sha256 hash algorithm is used to find the values of data.

Check out David Rugendyke’s article to get a better understanding of this mechanism.

Partially Upgradeable Strategies

Creating a fully upgradeable contract sounds great but there is a large trust compromise required: the immutability of the contract. Using only partially upgradeable contract systems may make sense in many cases.

In this strategy, core features of your smart contract can be left as non-upgradable. Other components that may be less-integral or more complex (and hence have high probability of requiring upgrade) are implemented with an upgradeable strategy.

I have seen a few good examples of this but if you know of any more please let me know:

The Ethereum Name Service “ENS”: The core ENS contract is a very simple contract and cannot be changed. Domain registrars (eg for the“.eth” domain) however can be upgraded by administrators. The registrar for the “.eth” domain is a contract factory for Deed contracts which are created one per deed, so that if a new domain manager is used, it can be relinked with all previous deeds and their state without much hassle.

The 0xProject: The 603 line core DEX (Decentralized exchange) smart contract can be fully upgraded while the proxy contracts (one for each user) remain the same. The 0x “proxy” contract (not the same as the proxy strategy mentioned above) contains the user funds and settings. For this reason it requires more trust and is not an upgradeable part of the 0x contract system.

Other Challenges

In all cases, a governance tradeoff is made that compromises the immutability of a smart contract. A strong governance strategy is also needed as part of an upgradeable strategy.

Creating an opt-in upgradeable smart contract system is possible and valuable to users but adds complexity.

Changes to Solidity compilers may break compatibility between new and old contracts.

There are gas overheads to consider when formulating an upgradeable strategy.

Conclusion

No one strategy is perfect and selecting the right strategy depends on the smart contract system to be implemented. All strategies are complex and smart contract designers should be very comfortable with their chosen upgradeable strategy to avoid security vulnerabilities.

My opinions

To create an upgradeable smart contract, the proxy mechanism seems the best well rounded strategy because it allows programmers to separate the upgradeable mechanism from their contract design and this makes things much easier to reason with and use, and will create less errors (which is a principal reason why we need upgradeable contracts in the first place).

Update June 2018: The Unstructure Storage pattern that is in the final stages of being released as part of ZeppelinOS appears to be the most mature and tested pattern, and will likely be widely adopted.

The Unstructure Storage pattern that is in the final stages of being released as part of ZeppelinOS appears to be the most mature and tested pattern, and will likely be widely adopted. The use of a hybrid partially upgradeable strategy where the simplest, core logic is immutable also a good idea to maintain strong trust with users.

Designing your non-upgradeable smart contract system first and then formulating an upgradeable strategy seems like a practical and ideal way to go about this.

Twitter: @theblockstalk

Indorse: indorse.io

Research References

General

Proxy Contracts

Separate Logic and Data Contracts

Separate Logic and Data Contracts with Data as Key-Value pairs