The Basics of Upgradable Proxy Contracts in Ethereum

When smart contracts are deployed into the Ethereum blockchain, they are immutable and therefore, not upgradable. By re-architecturing the code into different contracts, it is possible to allow logic upgrades while keeping the storage the same. In fact, upgradable smarts contracts are becoming popular and Jack Tanner had a good article explaining all the techniques used. However having said that, I also believe in circumstances where we should not allow contracts to be upgraded at all. A good example would be when launching an ICO, I don’t agree that the token logic should be upgradable because it would meant that the owner could go back on his/her promises.

Leaving the upgradability debate aside, I will be focusing on the proxy technique first made popular by nick johnson in this article.

The idea is to have 1 storage contract, 1 registry Contract and 1 logic Contract. Whenever there is a need to add a new or upgrade an existing function in the logic contract, just create a new logic contract, inheriting from the current one.

The storage contract simply holds the state and let’s make it as simple as possible:

pragma solidity ^0.4.21;

contract Storage {
uint public val;
}

The registry contract provides the proxy for the logic contract to modify the states of the storage contract it inherited.

pragma solidity ^0.4.21;

import './Ownable.sol';
import './Storage.sol';

contract Registry is Storage, Ownable {

address public logic_contract;

function setLogicContract(address _c) public onlyOwner returns (bool success){
logic_contract = _c;
return true;
}

function () payable public {
address target = logic_contract;
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize)
let result := delegatecall(gas, target, ptr, calldatasize, 0, 0)
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result
case 0 { revert(ptr, size) }
case 1 { return(ptr, size) }
}
}
}

The registry contract needs to have knowledge of which logic contract to talk to. We can set that up using the setLogicContract function. We have used a simple Ownable.sol to ensure that only admin can call the setLogicContract function. The fallback function in assembly might look unfamiliar to some but this particular code is actually quite standard for proxy contracts. Basically, its allowing an external contract to change its internal storage. Note that it is also very important to initialise the Storage before the Ownable contract. Getting this sequence wrong is disastrous. Guess why?

The delegatecall assembly code is convenient but also dangerous. So make sure you know what is happening before going live with it.

Next, let‘s’ talk about the logic part.

pragma solidity ^0.4.21;

import './Storage.sol';

contract LogicOne is Storage {
function setVal(uint _val) public returns (bool success) {
val = 2 * _val;
return true;
}
}

The code above is straight forward. The LogicOne contract is meant to modify the “val” storage.

Implementation

  1. We deploy both Registry.sol and LogicOne.sol
  2. We register LogicOne deployed address in Registry.sol, ie
Registry.at(Registry.address).setLogicContract(LogicOne.address)

3. We use LogicOne ABI to modify the “val” storage in Registry contract.

LogicOne.at(Registry.address).setVal(2)

4. When we are ready to upgrade LogicOne to LogicTwo for example, we deploy LogicTwo contract and update the Registry contract to point to it.

Registry.at(Registry.address).setLogicContract(LogicTwo.address)

5. We can now control Registry’s storage with LogicTwo.

LogicTwo.at(Registry.address).setVal(2)

It is important to take a bit of time to digest what we are doing here rather than jumping into the code right away. The Storage for LogicOne and LogicTwo are not the same as the one from Registry contract. They are related only by storage design (which is very important).

Conclusion

The code used in this article can be found in github and it’s mainly for conceptual explanation purpose only. I have reduced a lot of complexity and checks to make its a bare skeleton. If you intend to use it for production, I suggest you to do more due diligence on your end.

Happy coding!

Founder of Whale Tech. Blockchain Developer and Technologist.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store