Evolving ethereum smart contracts in production without dataloss
Tags: blockchain, ethereum
Published: 2020-06-01 18:20:53


# Evolving ethereum smart contracts in production


## Why do we need to evolve smart contracts

There can be multiple reasons for evolving a smart contract, you can find a bug in one of the methods and you need to fix it, you spotted a typo or a formatting issue and that needs to be fixed. However, the one we are interested in for this discussion is adding a new feature in your current contract.

In agile ways of working, we continuously keep evolving the functionalities iterations after iterations, so we need a way to achieve this somehow.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/u2tlf10zopbsgptg78i4.png)


## What is the issue in evolving the smart contract

In ethereum smart contracts are deployed as a transaction, the bytecode is extracted for a smart contract and that bytecode is sent as part of the transaction payload. And because of the immutable property of blockchain, you can not change the contract once it is deployed, meaning that once a contract is deployed you can not change anything in the contract, the only choice you are left with is to deploy a new version of the contract.

__PROBLEM : If we keep the data of the contract in the state variables, and we want to deploy a new version of the contract then the older state will be lost because we have now deployed a new contract.__ This is the main problem in evolving smart contracts in live data like in production.

Now consider a very basic contract for this discussion, The _Number contract_:

__<span style="text-decoration:underline;">ITERATION 1</span>__

```javascript
contract Number {

    uint private data;

    constructor(uint initVal) public {
        data = initVal;
    }

    function set(uint x) public {
       data = x;
    }

   function get() view public returns (uint retVal) {
        return data;
    }

}
```

Now, suppose we want to add a check in the setter method that negative value cannot be added in the contract, so the iteration 2 contract will look like this:

**<span style="text-decoration:underline;">ITERATION 2</span>**

```javascript
contract Number {

    uint private data;

    constructor(uint initVal) public {

        data = initVal;

    }

    function set(uint x) public {

       require(x>0,”Number can not be negative”);

       data = x;

    }

   function get() view public returns (uint retVal) {

        return data;

    }

}
```

There can be two different ways to evolve the Number contract keeping the data intact. __Keeping the data intact is the main problem that we are trying to resolve.__


# Maintaining smart contract’s state after deployment


## Using delegatecall

The main idea behind this approach is to split the Number contract into two different contracts, one contract I call the _<span style="text-decoration:underline;">proxyNumber</span>_ contract and the other one is _<span style="text-decoration:underline;">numberLogicIteration1</span> _contract.

The main idea in this approach is to make the logic of contract pluggable, and it can be replaced at any point of time, following diagram explain this:

## 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zj4dity8lyimuut8yfw2.png)


Lets see the code for the _<span style="text-decoration:underline;">proxyNumber</span>_ contract first:

```javascript
pragma solidity >=0.4.17 &lt;0.7.0;

contract ProxyNumber {

    uint private data;

    constructor(uint initVal) public {

        data = initVal;

    }

    function set(uint x , address logicContract) public {

        logicContract.delegatecall(abi.encodeWithSignature("set(uint256)",x));

    }

    

    function get() view public returns (uint retVal) {

        return data;

    }

}
```

The main idea is to use the delegateCall method, according to the [official solidity documentation](https://solidity.readthedocs.io/en/v0.4.21/introduction-to-smart-contracts.html#delegatecall-callcode-and-libraries) when we use the delegateCall on a contract address then _“the code at the target address is executed in the context of the calling contract and <code>msg.sender</code> and <code>msg.value</code> do not change their values”. </em>This means that logicContract’s set method will be invoked but the state will be mutated on the proxyNumberContract.

Let's look at the logicV1Contract:

**<span style="text-decoration:underline;">ITERATION 1</span>**

```javascript
pragma solidity >=0.4.22 &lt;0.7.0;

contract logicV1 {

    uint private data;

    function set(uint x) public {

       data = x;

    }

}
```

The only method here is the setter method for number contracts. **Basically we are dynamically loading code from the logicV1 smart contract at run time. Storage, current address and balance still refer to the ProxyNumber contract, only the code is taken from the logicV1 address**.

After iteration1, we have to add the validation as mentioned in the introduction, for that we will create one more logic contract like below:

**<span style="text-decoration:underline;">ITERATION 2</span>**

```javascript
pragma solidity >=0.4.22 &lt;0.7.0;

contract NumberProxy {

    uint private data;

    function set(uint x) public {

        require(x>0,"Negative numbers are not allowed");

        data = x;

    }

}
```

And from the proxy method all we need to do is change the address for the logic contract:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9geyha0s658arroslwoo.png)



# Separate state and logic contract

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 use and update this data. 

This splitting of Number contract allows the logic to be replaced while keeping the data in the same place, allowing for a fully upgradeable system. The address of the data contract can be injected into the logic contract keeping logic and data independent of each other.

The idea behind this approach is that the logic part of a smart contract is more susceptible to change and errors as compared to the data or state variables part of the contract. We need to register the data contract with the logic contract somehow, it can be done by passing the data contract address in the logic contract’s constructor or by using a setter for data contract in the logic contract.

One important thing about this approach is that the logic contract will be importing the abi for the data contract because we need to call the setter method. The delegateCall method used in earlier approaches doesn’t support returning any value so we can not use the delegateCall to return the value, hence we need to initialise the data contract using its address and abi.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o9ufyx3rkfwa8yf6x6uy.png)


Let's first see the code for the Data contract since it will remain same across iterations and is relatively simple:
```javascript

pragma solidity >=0.4.17 &lt;0.7.0;

contract NumberData {

    uint private data;

    constructor(uint initVal) public {

        data = initVal;

    }

    function set(uint x) public {

        data = x;

    }

    

    function get() view public returns (uint retVal) {

        return data;

    }

}
```

If you focus on the methods used in this contract, they are only getters and setter methods.

Now lets see the main logic contracts:

**<span style="text-decoration:underline;">ITERATION 1</span>**
```javascript

pragma solidity >=0.4.17 &lt;0.7.0;

import "./Number_Data.sol";

contract NumberLogic {

    NumberData private data;

    constructor(address dataContractAddr) public {

        data = NumberData(dataContractAddr);

    }

    function set(uint x) public {

        data.set(x);

    }

    

    function get() view public returns (uint retVal) {

        return data.get();

    }

}
```

Few points to notice:



1. We are importing the data number solidity file because we are calling the data contract directly from the logic contract.
2. We are initialising the data contract from the constructor of the number logic contract using NumberData(dataContractAddr);

Lets see how this will look in iteration 2:

**<span style="text-decoration:underline;">ITERATION 2</span>**
```javascript

pragma solidity >=0.4.17 &lt;0.7.0;

import "./Number_Data.sol";

contract NumberLogic {

    NumberData private data;

    constructor(address dataContractAddr) public {

        data = NumberData(dataContractAddr);

    }

    function set(uint x) public {

        require(x>0,"Negative numbers are not allowed");

        data.set(x);

    }

    

    function get() view public returns (uint retVal) {

        return data.get();

    }

}
```
_We can change the logic part of the contract anytime keeping the data still intact because data is kept on a separate data contract_. This is depicted below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o0agd5xb9rz13uokmllk.png)


That's all folks for today :)

All the code is available on Github, you can comment on this if you have any questions or comments.

Contracts : [https://github.com/MrHmP/upgradable-smart-contracts](https://github.com/MrHmP/upgradable-smart-contracts)