Understanding Upgradeable Smart Contracts hands-on.
Tags: web3, tutorial, solidity, openzeppelin
Published: 2022-06-27 20:41:36

Back with another article and this time, it will be a series of articles where we will cover a wide range of topics starting from Upgradeable Smart Contracts. This series is aimed at helping you understand how you can develop upgradable smart contracts – not just coding but what kind of a mindset is needed for it. This series will have articles dedicated to:
1.	Hands-on Development and Testing of Upgradeable Smart Contracts.
2.	Testing Upgradeable Smart Contracts.
3.      Deploying Upgradeable Smart Contracts.
4.	The Nuances of writing upgradeable smart contracts.
5.	A Duct-tape solution of sorts for using Chainlink’s Oracle contracts with Upgradeable Smart Contract.

I have this tendency of building upon previous mini-projects developed during articles. At this point, it feels like creating an NFT smart contract to demonstrate stuff is equivalent of Todo app. Every web developer learns to build a Todo app whenever testing out new framework.

This article will revolve around the first point above. My aim is to make this article a one-stop solution to understanding upgradeable smart contracts. We will first write a generic smart contract (sort of like a Hello World). Then we will upgrade it to show how business logic can be added via upgrades. The test driven approach for this will be discussed in the next article in this series. There is a lot to cover and so let’s get on with the show.

## Contracts
Let’s get busy with writing our smart contracts. At first we will use a simple smart contract – one which only has a greeting() function that returns a string. Then we will upgrade it to (and you know it) – an NFT Smart contract. 

### A Bland Version 1
You might think that the contract given below is not that important given it only returns a string. As Dwight put it

![Dwight](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3klalurbsfgytlya1vkd.gif)

Rest assured, upgrading smart contracts is not really identity-theft.

In the code below, you see the version 1 of our contract – aptly named `OurUpgradeableNFT1`. There is no “constructor” in the code below. **<u>Upgradable smart contracts don’t have a constructor</u>**. They have an initializer function (that’s literally what a constructor is though). We inherit from `Initializable`, `UUPSUpgradeable` and `OwnableUpgradeable` in our contract’s version 1 as shown below. These contracts which are inherited serve important purposes.

```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract OurUpgradeableNFT1 is
    Initializable,
    UUPSUpgradeable,
    OwnableUpgradeable
{
	string public greeting;

    function initialize(string calldata _greeting) public initializer {
		greeting = _greeting;
        __UUPSUpgradeable_init();
        __Ownable_init();
	}

    function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyOwner
    {}
}
```

`Intializable` provides us with the modifier `initializer`. This helps us in declaring our initializing function. **In fact, contrary to convention, the initializing function need not be called “initializer”**. You might as well name it “sallysAuntPolly” and it will still work. Shakespeare, thus, rightly say “What’s in the name?”[[ref](https://nosweatshakespeare.com/quotes/soliloquies/whats-in-a-name/)]. It’s not the name of the function rather its purpose that we need to focus on. The initializing function (`initialize` in the contract above) is supposed to be executed only once. The risk it brings at being invocable even after that is contract hijacking. `Initializable` provides us with the `initializer` modifier which makes sure that it can only be invoked once during contract deployment.

`UUPSUpgradeable` is for providing UUPS proxy-based upgrade functionality. `OurUpgradeableNFT1` also inherits the `_authorizeUpgrade()` function from it. This function needs to be present and overridden (if inherited) for UUPS proxy-based upgrade functionality. You need not have anything inside it – but still it needs to be there.  If you are not sure what that is, I will refer you to this awesome explanation video.

{% embed https://www.youtube.com/watch?v=kWUDTZhxKZI %}

`OwnableUpgradable` is the upgradable counterpart of `Ownable` contract from Openzeppelin used for access control. It provides a modifier `onlyOwner` whose functionality you can very well guess. Also, like the normal `Ownable`, it provides a `owner()` function that returns the address of the owner along with other functionalities which include relinquishing ownership and such. This is important because we want that only the owner should be able to upgrade the contract. This is reflected on the `_authorizeUpgrade()` function which contains the `onlyOwner` modifier in the contract above.

Our `initialize` function in the code above initializes the `UUPSUpgradeable` and `OwnableUpgradeable` contracts with `__UUPSUpgradeable_init()` and `__Ownable_init()` respectively. The former need not be initialized at the time of writing. Our initialize function also receives a string via `_greeting` parameter during initialization which it assigns to a public storage variable called `greeting`. 

There is also an easily missed demonstration in the code above which further differentiates constructor in normal smart contracts and initializer in upgradable ones. Notice how the `_greeting` parameter is defined as `**calldata**`. <u>If this were a constructor, we would have to use `**memory**` as constructors</u> are not allowed to have `calldata` type parameters.

This is mostly it for our version 1. Version 2 is where exciting part arrives.

### A slightly less Bland Version 2
Now in the upgrade contract (version 2) we will make our contract an NFT minting contract. Normally, this can be accomplished just by inheriting from [`ERC721`](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721) or [`ERC721URIStorage`](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#ERC721URIStorage) but 

![We don't do that here](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m4hys17wurs4tztievfi.jpg)

In the code below, our version 2 inherits from `ERC721URIStorageUpgradable`. This contract is mostly identical to the normal Openzeppelin contract with the exception of the constructor being replaced by initializer. If you have used `ERC721URIStorage`, you should know by now that it inherits from `ERC721` so we do not need to inherit from `ERC721`. Case is similar here. The interesting thing to note here is that we inherit from our previous version i.e. `OurUpgradeableNFT1`. Now...

![Wait a minute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmlg0pttj83wob5fykoh.gif)

_Aren’t we supposed to upgrade that stuff? Why are we inheriting from it? Does that mean that we cannot really replace that code completely?_ The answer to that is tricky.

Upgradeable Smart contracts share the same storage space. A fact you might be aware of if you watched the video above. This also implies that any “upgrade” we make cannot really delete stuff from that storage but add to it. The functions in the previous contract are a different story though and we will discuss that in a little bit.

```javascript
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";

import "./NFTContract1.sol";

contract OurUpgradeableNFT2 is OurUpgradeableNFT1, ERC721URIStorageUpgradeable {
    using CountersUpgradeable for CountersUpgradeable.Counter;

    CountersUpgradeable.Counter private _tokenIdCounter;

    function reInitialize() public reinitializer(2) {
        __ERC721_init("OurUpgradeableNFT", "OUN");
        __ERC721URIStorage_init();
    }

/*
* Remaining Contract Code here
*/

}
```

In the lines following up to the `reInitialize()` function above, we inherit from our previous contract (version 1) and from the `ERC721URIStorageUpgradeable` and we define a counter for keeping track of our NFTs. It’s the reinitialize function that needs more attention. Notice how it contains this modifier `reInitializer(2)`. Where did that come from?

You can search in `ERC721URIStorageUpgradeable` but you won’t find it. This comes from the `Initializable` contract we discussed in the previous sub-section. This multi-level inheritance in our smart contract gives us access to that. It is used for deploying upgraded versions. Given that initializer functions replace constructors, it stands to reason that we might need to invoke one every version. The initializer from previous version will be invoked automatically while upgrading the new contract. But we cannot modify that. So, we specify a new one. `reinitializer(2)` means that this function can only be invoked in version 2.

Points to note here:-
1.	Inheriting from `Initializable` in previous contract automatically assigns it version 1.
2.	The re-initializer in version 2 needs to be manually invoked. Placing `reInitializer(2)` modifier just restricts scope of invocation – it does not mean that the function with that modifier will automatically get invoked in version 2.

You might think that perhaps if we inherit from `Initializable`, `UUPSUpgradeable` and `OwnableUpgradeable` then we might not need to inherit from `OurUpgradeableNFT1`. That is not possible here and we will discuss that in detail in the 3rd article. In the code section below, the rest of the functions we will put into our version are given. We will put these functions in the section of the code above where it says "Remaining Contract Code here".

```javascript

    function greetingNew() public pure returns (string memory) {
        return "New Upgradeable World!";
    }

    function safeMint(address to, string memory uri) public virtual onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

```
The `greetingNew()` function is similar to the `greeting()` function in version 1. The purpose of this function is to show how both of them will co-exist in version 1.

The remaining functions should appear quite generic in nature. If you have used the wonderful that Openzeppelin wizard is, then you will already be familiar with them.

The thing you need to need to notice at this point is that the `safeMint()` function is declared as `virtual`. This means that any contract inheriting from OurUpgreadableNFT2 contract will have the option to either:

1. Implement a new logic for minting NFTS.
2. Sticking to the old implementation with the help of `super`.

_This paves way for upgradeability in our contract_ - something that you need to remember when developing upgradable smart contracts.

This completes the coding of smart contracts. While I would love to cover testing and deployment in this article, it might be a too much of an overload for a first-timer. Instead, I would recommend reading through again and noting down few of the nuances of upgradable smart contracts that we have discussed here in this article.

## Conclusion
If you have understood up to this point and you are indeed a first-timer wishing to learn how to develop upgradeable smart contracts then kudos to you. If there are still some gaps left which you would like me to fill out then feel free to drop your doubts in the comment section below.

In the next article in this series we will go through how you can test your upgradeable smart contracts and then deploy them. Until then, continue to make awesome things on Web3 and WAGMI!