Reader small image

You're reading from  Solidity Programming Essentials. - Second Edition

Product typeBook
Published inJun 2022
PublisherPackt
ISBN-139781803231181
Edition2nd Edition
Concepts
Right arrow
Author (1)
Ritesh Modi
Ritesh Modi
author image
Ritesh Modi

Ritesh Modi is a technologist with more than 18 years of experience. He holds a master's degree in science in AI/ML from LJMU. He has been recognized as a Microsoft Regional Director for his contributions to building tech communities, products, and services. He has published more than 10 tech books in the past and is a cloud architect, speaker, and leader who is popular for his contributions to data centers, Azure, Kubernetes, blockchain, cognitive services, DevOps, AI, and automation.
Read more about Ritesh Modi

Right arrow

Chapter 12: Upgradable Smart Contracts

Writing upgradable contracts is an essential design pattern for contracts supported by blockchain. It is necessary to be familiar with and implement this pattern in relation to any contracts that developers think may need to be changed in the future. In this chapter, we will look at various ways to write upgradable smart contracts. We will learn what proxy contracts are, how to implement them for function upgradability using object-oriented concepts such as inheritance and composition, and finally learn about the patterns related to upgradable storage within smart contracts. Related concepts, such as dependency injection, will also be covered as part of the chapter.

We will cover the following topics:

  • Learning what constitutes upgradability
  • Understanding dependency injection
  • Reviewing problematic smart contracts
  • Implementing simple solutions with inheritance
  • Implementing simple solutions with composition
  • Implementing...

Technical requirements

To follow the instructions in this chapter, you will need the following:

  • A Chrome or Firefox browser
  • Access to the Remix website

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Solidity-Programming-Essentials-Second-Edition/tree/main/Chapter12.

Learning what constitutes upgradability

We all download and use various software. We also consume software as a service that is hosted by a provider. In any case, as a developer, we bind ourselves to a particular version of the software/service. For example, when you download Geth to run an Ethereum node, you download a specific version of Geth and use the features provided by that version. There could be multiple versions available prior to the downloaded version and they might have many similar, or, at the same time, not so similar, features. What this means is that every time there is a new version of any software/service, there are generally significant changes and improvements over the prior version.

These significant changes and improvements could be in the form of the introduction of new features, existing feature enhancement, bug fixes, issue resolution, or a variety of other reasons typically related to software, such as base platform upgrades and fixes.

We know...

Understanding dependency injection

When we have dependencies between smart contracts, the dependent smart contract would need an instance of the independent smart contract to invoke its function. There are two choices in such cases: either the smart contract itself creates a new instance of the contract, or it might already expect the smart contract instance to be available. If the dependent smart contract creates a new instance, it will have an instance address directly available using the new keyword, and if it expects the instance to be already available, then the address of the contract instance must be supplied to the dependent smart contract.

Supplying the address of the independent smart contract instance is also known as injection or, in other words, the dependencies of a smart contract are injected at runtime rather than the dependencies getting created at design time. Using the new keyword is a design-time static creation of a smart contract.

The dependencies or independent...

Reviewing problematic smart contracts

Before we write upgradable smart contracts, let's write a simple smart contract that we would like to update in the future as part of enhancements. Using this smart contract, we will understand the problems encountered when we want to upgrade it. The smart contract is called a Bank contract, with a couple of function implementations to debit and/or credit an account. It maintains a global state for each account along with its balance in a mapping data type. Since the mapping has public visibility, a getter function is intrinsically generated to access the account balance. There is also a constructor that would initially assign a specified balance to the bank's address. Since the bank is deploying this contract, the value of msg.sender in the constructor will have the value of the bank's address:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0
contract Bank {
    
   ...

Implementing simple solutions with inheritance

We will continue to use the previous Bank and BankClient smart contracts and keep improving them to incorporate upgradability therein.

This solution abstracts the storage or state variables from the smart contract and places them in a different smart contract. Having state and functionality in two separate smart contracts assists in reusing existing smart contracts using the dependency injection pattern that we learned about in the Understanding dependency injection section.

In the next example, a new smart contract named BankStorage is listed. This contract just has a declaration for the storage variables. They can additionally have functions whose purpose is to provide read-write functionality for the storage variables. The previous Bank contract has been refactored by removing the state variable to the BankStorage contract.

Next, we create the Bank contract; however, this time we inherit from the BankStorage contract. Inheriting...

Implementing simple solutions with composition

The previous section showed how to create a simple upgradable solution using inheritance. The same can be achieved using composition as well. Composition is a well-stable object-orientation concept in which objects are created by composing other objects. The other objects can live independently or can be completely dependent on the parent object.

The same example from the previous section has been used here, but refactored to use composition instead of inheritance. In this example, the BankStorage contract has the storage variable for storing global data along with a couple of getter and setter functions for reading and writing the state variables, as shown here:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0
contract BankStorage {
    mapping (address => uint256) public balances ;
    
    function SetBalance(address addr, uint256 amount) public...

Implementing advanced solutions using proxy contracts

The word proxy means on behalf of. Proxy is one of the established patterns in the design pattern world. Generally, functions are directly invoked in a smart contract. This induces tight coupling between the caller and the contract called. To induce upgradability and manageability, this direct link between contracts should be removed and an additional abstraction called proxy contracts introduced instead. The proxy contract stays in between the caller and called contract and ensures that a two-way request-response operation can be handled between them.

In a proxy contract implementation, the caller does not invoke functions directly on the target contract. Instead, the caller knows about the proxy contract and invokes functions on the proxy contract. It is the job of the proxy contract to take the request from the caller contract, optionally modify the request, and invoke the actual function from the main contract implementing...

Writing upgradable contracts with upgradable storage

All examples and techniques shown so far in this chapter were dealing with future changes in contract functions. The changes to the functions are generally more than the storage variables. However, the contract variable may require changes. In such cases, there is an additional design pattern that should be implemented alongside the others that were shown earlier in this chapter. This pattern helps in creating upgradable storage variables.

Storage variables are stored in persistent storage, with dynamic types such as arrays and mappings treated differently from native types such as Booleans and integers. Native types are stored in sequence one after another, while arrays and mappings are stored at different locations. Instead of placing variables consecutively in sequence, we can determine the location of these variables dynamically and store our data there. We can create some placeholder variables to start with and store newer...

Summary

This chapter was a deep dive into writing smart contracts that can upgrade and evolve in the future. Dependency injection is an important concept for writing upgradable smart contracts, and this was also explained at the beginning of the chapter. Almost every contract will change at some point in time and will need to be upgraded. Writing upgradable contracts is generally a non-functional requirement for any mission-critical contract and a must-have skill for any serious smart contract author. In this chapter, we saw multiple ways of writing upgradable smart contracts, from simple implementations to complex approaches involving assembly code.

In the next chapter, we will get into the details of writing secure smart contracts. It is one of the most important aspects of writing quality smart contracts and plays an important role in taking them to production.

Questions

  1. What are the different ways through which a contract address can be supplied to another contract and why would you do that?
  2. Is it possible to implement upgradable smart contracts with upgradable storage?
  3. Why do we need upgradable smart contracts in the first place?
lock icon
The rest of the chapter is locked
You have been reading a chapter from
Solidity Programming Essentials. - Second Edition
Published in: Jun 2022Publisher: PacktISBN-13: 9781803231181
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Ritesh Modi

Ritesh Modi is a technologist with more than 18 years of experience. He holds a master's degree in science in AI/ML from LJMU. He has been recognized as a Microsoft Regional Director for his contributions to building tech communities, products, and services. He has published more than 10 tech books in the past and is a cloud architect, speaker, and leader who is popular for his contributions to data centers, Azure, Kubernetes, blockchain, cognitive services, DevOps, AI, and automation.
Read more about Ritesh Modi