Updating Smart Contracts

Published: April 27, 2018, updated: January 4, 2025

Update from 2025: the cryptocurrency boom of 2017 and 2017 and ICOs were an interesting time, huh? It’s not my cup of tea anymore. At the same time, I’d like for this post to stand here as a historical artifact, and encourage you to spend your time on more worthwhile things instead.

Thank you for reading my disclaimer. Please enjoy the following post:

As time goes on, smart contract requirements change. Often, a feature is added or a bug is found. But, once a smart contract has been deployed on the blockchain, it can’t be upgraded. The entire value proposition of a blockchain is immutability. Yet, in the real world, contracts change all the time. For example, a labor contract changes when new labor laws are introduced. Similarly, there are many circumstances under which smart contracts change as well, such as

A contract itself cannot be replaced while still keeping the same contract address on the blockchain. A few methods exist that all use some kind of indirection that allow smart contracts to be replaced with newer versions and still use the same address.

1. Contract relay

A contract relay forwards all incoming calls to a designated address using the DELEGATECALL opcode.

An example implementation can be seen in this Stack Exchange post.

The main idea is to define a fallback function that forwards all function calls.

contract Relay {
    address contract;

    // ...

    function update(address _newContract) {
        contract = _newContract;
    }

    //...

    function(){
        if(!contract.delegatecall(msg.data)) throw;
    }
}

contract.delegatecall(msg.data) forwards all transaction data, including msg.sender. For the receiver of the delegate call, the incoming call looks like the user placed it there themselves.

The preceding Stack Exchange post also links to a fuller implementation on GitHub. It addresses the problem of how to handle the contract’s internal state when upgrading to a new version. Simply pointing the contract address variable to a new contract means that the old contract’s data is not copied over.

2. Contract registry

The other approach to updating contracts is to break up contracts into smaller pieces that can be exchanged easily. For this, each part needs to stick with a specific interface. If newer versions of the contract stick to the same interface, the caller does not have to worry about internal changes of the contract and can have static guarantees about the external behavior of this contract.

interface ContractInterface {
    // ... define your contract's interface here
}

Then, we create the contract that we want to eventually upgrade.

contract Contract implements ContractInterface {
    // ... first implementation of the contract
}

The Registry contract contains all the small contract parts that can be updated. Here, we allow the OpenZeppelin owner of the Registry to set the new version.

// ... import Ownable.sol

contract Registry is Ownable {
    ContractInterface public contract;


    // In this case, only the contract owner can upgrade the contract
    setContract(ContractInterface _contract) ownerOnly{
        contract = _contract;
    }
}

Then, in web3 we can add the contract to the registry.

// ... deploy the new contract
// ... find out the contract address and add it to the registry
await registry.methods.setContract(contractAddress).send();
// ... now we will get the contract address to use it
contractAddress = await registry.methods.contract();

Let’s say that after a while you create a new contract and what the registry to point to it. First, the contract is defined using exactly the same interface as before.

contract NewContract implements ContractInterface {
    // ... new implementation
}

As soon as the contract is deployed, we—the owner of the Registry—can now point to the new contract by calling the .setContract() method with the new contract’s address.

// ... deploy the new contract
// ... find out the contract address
registry.methods.setContract(newContractAddress).send();

Tags

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index