187

If the contract issuer wants to have a way to upgrade the contract code, so that account data and other things carry over, can Ethereum provide this? Also can this be done without changing the contract address or does one always need to deploy a new contract?

Do "annex" mechanisms exist to add some new functionality to a contract without a total rewrite?

3

13 Answers 13

34

Yes. There are a number of approaches using which you can upgrade a Contract1 to Contract2, keeping its state(data & balance) with the same address as before.

How does this work? A way is to use a proxy contract with a fallback function where each method call/trx is delegated to the implementation contract (which contains all the logic). enter image description here

A delegate call is similar to a regular call, except that all code is executed in the context of the caller (proxy), not of the callee (implementation). Because of this, a transfer in the implementation contract’s code will transfer the proxy’s balance, and any reads or writes to the contract storage will read or write from the proxy’s storage.

In this approach, users only interact with the proxy contract and we can change the implementation contract while keeping the same proxy contract.

enter image description here

The fallback function will execute on any request, redirecting the request to the implementation and returning the resulting value (using opcodes).

This was a basic explanation which is enough for us to work with upgradeable contracts. In case, you want to dig deep into proxy contract code and different proxy patterns, then check out these posts.

How can I write upgradable smart contracts?

OpenZeppelin provides awesome CLI tools & JS Libraries that take care of all the above complex proxy contracts, linking it to implementation (logic) contract & managing all the contracts you deploy using the CLI for upgradability, out-of-the-box.

The only thing you need to do is to write your contracts, and use OpenZeppelin CLI or Libraries to deploy the contracts.

NOTE: There are a few Limitations that you should be aware of, in terms of how you need to write your contracts and how you should upgrade them. There are also a number of workarounds these limitations in this post.

4
  • 3
    In this pattern what stops a malicious bait and switch? I.e. you buy your tokens, trusting the balance, then later on the developer switches to a new contract where the tokens are taken away from you.
    – Tom
    Jan 18, 2021 at 18:58
  • Don't buy the tokens in the first place unless you trust the developer with the single upgrade the right thing. A good DAO project will have the upgrade proxy keys controlled by a multisig or by direct DAO voting. But if you are worried about malicious bait and switch then you probably should not trust the project in the first place as there are thousands of other ways to screw you up. Mar 13, 2021 at 9:04
  • I would also clarify that you can easily deploy the upgrade proxy contracts yourself - you can find an example here: github.com/Dawn-Protocol/dawn-erc20-erc777 Mar 13, 2021 at 9:06
  • Do the proxy contract and the implementation contract both exist on the chain with separate addresses? If a contract is upgraded, can people still call the older contract directly bypassing the proxy? Feb 20, 2022 at 16:38
144

Once a contract is in the blockchain, it is final and cannot be changed. Certain parameters, of course, can be changed if they are allowed to change via the original code.

One method of updating contracts is to use a versioning system. For example, you could have an entryway contract that just forwards all calls to the most recent version of the contract, as defined by an updatable address parameter. You could also use a name registry, and update that to point to the most recent contract version.

Another method is to put your logic code in a library, then use the CALLCODE feature, via libraries in Solidity, to call the code located at a specified, updatable, address. This way, user data persists between versions. This has the limitation that the ABI of the logic contract must stay the same over time.

Here's an old gist I used to demonstrate data/code segregation a while ago.

Homestead Edit:

Starting with the Homestead release, there is now a DELEGATECALL opcode. This allows you to essentially forward calls to a separate contract while maintaining msg.sender and all storage.

For example, you could have a contract that maintains the same address and storage, but forward all calls to an address stored in a variable:

contract Relay {
    address public currentVersion;
    address public owner;

    function Relay(address initAddr){
        currentVersion = initAddr;
        owner = msg.sender;
    }

    function update(address newAddress){
        if(msg.sender != owner) throw;
        currentVersion = newAddress;
    }

    function(){
        if(!currentVersion.delegatecall(msg.data)) throw;
    }
}
17
  • It might be worth mentioning name resolvers, too. May 19, 2016 at 8:51
  • 2
    Here's a great example of this idea fully fleshed out: gist.github.com/Arachnid/4ca9da48d51e23e5cfe0f0e14dd6318f Jan 29, 2017 at 20:33
  • 1
    This is so smart, and is a form of dependency injection: the entryway contact is dependent on the current version's address. Jun 12, 2017 at 23:44
  • Hey Tjaden Hess, Jossie Calderon ! I'm a bit confused about how to actually implement this concept. I've made a gist with a simple example with a single contract Donation that needs to be updated. Would someone be kind enough to take a look at it and tell me if I'm completely misunderstanding the concept? gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118 Appreciate this a lot, thanks !
    – fabdarice
    Jun 14, 2017 at 18:56
  • Hey Tjaden Hess nice example, How can I deal with this contract through web3, Suppose I have contract named contract DemoVersion1.sol which contain following code contract DemoVersion1 { function checkVersion() returns (uint){ return 1; } } and I have to upgrade the contract to version two which contain following code contract DemoVersion2 { function checkVersion() returns (uint){ return 2; } } how can I handle calling the methods of contract , can any one explain or point to the suitable example. Jun 15, 2017 at 9:02
36

One method is to use a System of Contracts as outlined below:

  1. Contract "Register" - will contain pairs "name - address" for all contracts of your system;
  2. Contract Backend;
  3. Contract Frontend using Backend;
  4. Deploy Register & get address of it;
  5. Deploy Backend & register address of Backend into already deployed Register;
  6. Hardcode the address of Register into source of Backend. Before any call Backend from Frontend you should call your Register and get the actual address of Backend.

Then you can update your Backend contract any time - simply deploy the new one and re-register them in the Register.

Calling external contract: solidity.readthedocs.org...

Also see forum discussion: forum.ethereum.org...


UPD: Same but more efficient way (maybe)

First deploy:

  1. Write contract Register that can deploy other contracts with itself address as constructor argument;
  2. Write all other contracts - "upgradable" contracts with constructors requiring Register's address;
    • maybe that contracts should be disablable or have subside method
  3. Deploy Register giving to its constructor data - all other contract from step 2.

Upgrade:

  1. Deploy new version of "upgradable" contract with same address of Register;
    • Or maybe if your Register can deploy other contracts - git it to him
  2. (optional) disable/kill old version of "upgradable" contract;
  3. Register address of the new version of "upgradable" contract in the Register.
5
  • Great! If anyone has existing coded examples please add here :) Mar 31, 2016 at 13:44
  • I am moving the correct answer mark, because the other answer contains updated information on regarding how to do a relay contract on Homestead . Jul 7, 2016 at 15:12
  • 1
    Question: In the case of multiple contracts, wouldn't it be more efficient to first register all the contracts, save all their addresses and then deploy the Register contract (passing all the saved addresses as constructor arguments)? Might save a lot of function calls that way and corresponding gas? Jul 7, 2018 at 7:45
  • 1
    @BharatMallapur, yes off course
    – Alex Koz.
    Jul 7, 2018 at 17:50
32

Contract code is immutable, the storage is mutable, but you cannot execute code placed into storage, at least for now.

Bugfixes to contracts

As for bugfixes, the common pattern is to have proxy or lookup contracts to be a gateway to the real one, which in case of a change or bugfix would be replaced. Replacing it also means losing the old storage contents.

Keeping storage

If you want the ability to upgrade code, while keeping storage, you could think of separating storage and logic. Have a dedicated storage contract, which accepts write calls from trusted addresses (e.g. the logic contracts). All important storage should be associated with this one.

Accessing storage after selfdestruct

As of today there is no real pruning implemented even in the case of selfdestruct, but that should definitely come in the future. There are several EIPs discussing this.

Even if pruning is implemented, it shouldn't happen in an instant and you should be able to read storage from the last state. It is also planned to have archive nodes to keep states indefinitely -- not sure that is feasible without limitations just by judging at the growth of the blockchain.

Redeploying at same address

In short: practically this is not possible. The contract addresses are calculated from the sender and the nonce. The nonce is sequential, there cannot be any gaps and there cannot be duplicates.

In theory it is possible to arrive at the same hash with a different nonce and address combination, but the likelyhood is small.

23

Contracts deployed on a blockchain are immutable, so this means:

  • address and code of a deployed contract cannot be changed
  • deploying a newer (or even identical) contract will create a new address
  • code cannot be added to a deployed contract

If the contract issues want to have a way to upgrade the contract code, so that account data and other things carry over what means Ethereum provides for this?

A simple way to extend a contract C1, is to make sure that C1 has functions / accessors that return all data it has. A new contract C2 can be written, that calls the C1 functions and does additional or corrected logic. (Note, that if C1 and C2 have foo, where C1's foo is buggy and C2's foo is corrected, there is no way to disable C1 foo from being called.)

A registry can be used, as described by @Alexander's answer, so that other DApps and contracts query the registry for the address of contractC, so that when C1 is "replaced" by C2, no DApp code needs to change. Using a registry in this way prevents hardcoding the address of C1 (so that C2, C3, C4 can take its place when needed), but the DApp does need to hardcode the address of the registry.


EDIT: The ENS, Ethereum Name Service, was just deployed on the testnet (Ropsten).

See the ENS wiki for a quickstart and other details. Here's an introduction:

ENS is the Ethereum Name Service, a distributed, extensible naming system based on the Ethereum blockchain.

ENS can be used to resolve a wide variety of resources. The initial standard for ENS defines resolution for Ethereum addresses, but the system is extensible by design, allowing more resource types to be resolved in future without the core components of ENS requiring upgrades.

ENS is deployed on the Ropsten testnet at 0x112234455c3a32fd11230c42e7bccd4a84e02010.

Initial discussion here.

2
  • 1
    And the registry, as any indirection system, has its own bugs, security issues, and problems. If you want to upgrade the contract because of a security risk, this is something to keep in mind. If The DAO had an upgrade system, no doubt it would have been used for a hack...
    – bortzmeyer
    Jul 6, 2016 at 10:15
  • @bortzmeyer Agree, any upgrade mechanism has risks that could be exploited itself and those risks should be considered.
    – eth
    Nov 24, 2016 at 7:34
19

The most voted answer is to use delegatecall and it is very tricky to get correct.

https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns describes some upgrade methods, as well as critical considerations so that you don't introduce more bugs or a buggy upgrade method that doesn't work.

Proxy pattern recommendations

Check for the destination contract’s existence prior to calling delegatecall. Solidity will not perform this check on your behalf. Neglecting the check may lead to unintended behavior and security issues. You are responsible for these checks if relying upon low-level functionality.

If you are using the proxy pattern, you must:

Have a detailed understanding of Ethereum internals, including the precise mechanics of delegatecall and detailed knowledge of Solidity and EVM internals.

Carefully consider the order of inheritance, as it impacts the memory layout.

Carefully consider the order in which variables are declared. For example, variable shadowing, or even type changes (as noted below) can impact the programmer’s intent when interacting with delegatecall.

Be aware that the compiler may use padding and/or pack variables together. For example, if two consecutive uint256 are changed to two uint8, the compiler can store the two variables in one slot instead of two.

Confirm that the variables’ memory layout is respected if a different version of solc is used or if different optimizations are enabled. Different versions of solc compute storage offsets in different ways. The storage order of variables may impact gas costs, memory layout, and thus the result of delegatecall.

Carefully consider the contract’s initialization. According to the proxy variant, state variables may not be initializable during construction. As a result, there is a potential race condition during initialization that needs to be mitigated.

Carefully consider names of functions in the proxy to avoid function-name collision. Proxy functions with the same Keccak hash as the intended function will be called instead, which could lead to unpredictable or malicious behavior.

5
  • Thank you for the heads up. As this is the latest and most informative answer, although link only answer, I'll mark this as a correct one because the audience need to understand in detail all aspects of upgradeability. Sep 7, 2018 at 8:21
  • Can we add new functions into the upgraded contract? @eth
    – alper
    Mar 11, 2019 at 15:01
  • @alper A simpler way than delegatecall is to use ENS: ethereum.stackexchange.com/questions/77520/…
    – eth
    Nov 16, 2019 at 13:24
  • I am moving the correct answer marker, as OpenZeppelin now provides extensive documentation and tools around this. Mar 9, 2020 at 18:59
  • @MikkoOhtamaa No problem. The poster of the answer you've accepted posted it as an answer to a couple other questions too.
    – eth
    Mar 12, 2020 at 5:47
14

@Nick Johnson has a base contract for upgradeable contracts.

As he says, before using one should be "fully understanding the limitations and drawbacks."

/**
 * Base contract that all upgradeable contracts should use.
 * 
 * Contracts implementing this interface are all called using delegatecall from
 * a dispatcher. As a result, the _sizes and _dest variables are shared with the
 * dispatcher contract, which allows the called contract to update these at will.
 * 
 * _sizes is a map of function signatures to return value sizes. Due to EVM
 * limitations, these need to be populated by the target contract, so the
 * dispatcher knows how many bytes of data to return from called functions.
 * Unfortunately, this makes variable-length return values impossible.
 * 
 * _dest is the address of the contract currently implementing all the
 * functionality of the composite contract. Contracts should update this by
 * calling the internal function `replace`, which updates _dest and calls
 * `initialize()` on the new contract.
 * 
 * When upgrading a contract, restrictions on permissible changes to the set of
 * storage variables must be observed. New variables may be added, but existing
 * ones may not be deleted or replaced. Changing variable names is acceptable.
 * Structs in arrays may not be modified, but structs in maps can be, following
 * the same rules described above.
 */
contract Upgradeable {
    mapping(bytes4=>uint32) _sizes;
    address _dest;

    /**
     * This function is called using delegatecall from the dispatcher when the
     * target contract is first initialized. It should use this opportunity to
     * insert any return data sizes in _sizes, and perform any other upgrades
     * necessary to change over from the old contract implementation (if any).
     * 
     * Implementers of this function should either perform strictly harmless,
     * idempotent operations like setting return sizes, or use some form of
     * access control, to prevent outside callers.
     */
    function initialize();

    /**
     * Performs a handover to a new implementing contract.
     */
    function replace(address target) internal {
        _dest = target;
        target.delegatecall(bytes4(sha3("initialize()")));
    }
}

/**
 * The dispatcher is a minimal 'shim' that dispatches calls to a targeted
 * contract. Calls are made using 'delegatecall', meaning all storage and value
 * is kept on the dispatcher. As a result, when the target is updated, the new
 * contract inherits all the stored data and value from the old contract.
 */
contract Dispatcher is Upgradeable {
    function Dispatcher(address target) {
        replace(target);
    }

    function initialize() {
        // Should only be called by on target contracts, not on the dispatcher
        throw;
    }

    function() {
        bytes4 sig;
        assembly { sig := calldataload(0) }
        var len = _sizes[sig];
        var target = _dest;

        assembly {
            // return _dest.delegatecall(msg.data)
            calldatacopy(0x0, 0x0, calldatasize)
            delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len)
            return(0, len)
        }
    }
}

contract Example is Upgradeable {
    uint _value;

    function initialize() {
        _sizes[bytes4(sha3("getUint()"))] = 32;
    }

    function getUint() returns (uint) {
        return _value;
    }

    function setUint(uint value) {
        _value = value;
    }
}
2
  • This is my favorite solution. the answr should be upvoted more! With the last fork, there is also no need for specifiying the sizes of the return values (_sizes[bytes4(sha3("getUint()"))] = 32).
    – ivicaa
    Feb 27, 2018 at 16:05
  • I covered this topic here: youtube.com/watch?v=KBqDYF5jw-0
    – rstormsf
    Jun 8, 2018 at 7:24
9

Coming to one of the basic principles at Ethereum that is a Smart Contract cannot be modified after deployment.

BUT, you can still have Upgradable Smart Contracts if you put the following into consideration

This has to be planed from the start. The key point is number 4. But all the others are essential to have a real and smooth Smart Contract Upgrade.

So, you will need to design your smart contract taking in consideration the following 5 points:

  1. Keep your smart contracts modular and fairly separate rules and logic from data structure. So if you will need to change something, you will change just the related contract and you will not need to change many or all contracts.
  2. You should be prepared by having an emergency stop or circuit-breaker to be able to stop all operations during any migration. Because you do not want to be in a situation where people can still update/insert data to the old version of the smart contract while you are migrating and thereafter.
  3. You should previously provided the ability to read all the data from your smart contract. Of course you can do a permissioned read by restricting reading all data to the owner or any other trusted user or even another smart contract. You will need to read from the old version of your smart contract and insert in the new version.
  4. You will use of of the following strategies to communicate with your smart contract. I copied them from Smart Contact Best Practices:

Upgrading Broken Contracts

Code will need to be changed if errors are discovered or if improvements need to be made. It is no good to discover a bug, but have no way to deal with it

...

However, there are two basic approaches that are most commonly used. The simpler of the two is to have a registry contract that holds the address of the latest version of the contract. A more seamless approach for contract users is to have a contract that forwards calls and data onto the latest version of the contract.

Example 1: Use a registry contract to store latest version of a contract

In this example, the calls aren't forwarded, so users should fetch the current address each time before interacting with it.

contract SomeRegister {
    address backendContract;
    address[] previousBackends;
    address owner;

    function SomeRegister() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner)
        _;
    }

    function changeBackend(address newBackend) public
    onlyOwner()
    returns (bool)
    {
        if(newBackend != backendContract) {
            previousBackends.push(backendContract);
            backendContract = newBackend;
            return true;
        }

        return false;
    }
}

There are two main disadvantages to this approach:

  1. Users must always look up the current address, and anyone who fails to do so risks using an old version of the contract

  2. You will need to think carefully about how to deal with the contract data when you replace the contract

The alternate approach is to have a contract forward calls and data to the latest version of the contract:

Example 2: Use a DELEGATECALL to forward data and calls

contract Relay {
    address public currentVersion;
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function Relay(address initAddr) {
        currentVersion = initAddr;
        owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
    }

    function changeContract(address newVersion) public
    onlyOwner()
    {
        currentVersion = newVersion;
    }

    function() {
        require(currentVersion.delegatecall(msg.data));
    }
}

This approach avoids the previous problems but has problems of its own. You must be extremely careful with how you store data in this contract. If your new contract has a different storage layout than the first, your data may end up corrupted. Additionally, this simple version of the pattern cannot return values from functions, only forward them, which limits its applicability. (More complex implementations attempt to solve this with in-line assembly code and a registry of return sizes.)

Regardless of your approach, it is important to have some way to upgrade your contracts, or they will become unusable when the inevitable bugs are discovered in them.

However, I also recommend checking Proxy Libraries in Solidity that is posted by Zeppelin Solutions and Aragon. There is a planning to make an industry standard for this matter.

  1. You have to have a good testing strategies and tactics. Because the cost of updating the your smart contract can really ruin your life.

I created a story on Medium for this with title: Essential Design Consideration for Ethereum dApps (1): Upgradeable Smart Contracts and I provided a sample for every point of the above 5.

6

We(I and My team) have recently worked on Upgradable Contracts problem after referring to colony.io's post on Upgradable Contracts. So, we came with a solution in which we have different layers of contract rather than having one single Contract.

If I describe it briefly then one need to make the storage part very generic so that once you create it, you can store every type of data into it (with the help of setter methods) and access it (with the help of getter methods). That makes your data storage eternal, which you don't need to change in future.

Look at this datastore contract to understand it better - https://goo.gl/aLmvJ5

Second layer should be the main contract with your functions, which can be upgraded at later point and in order to use the old data store, you should make the contract in way so that you can point your newly deployed contract to the existing(old) data store and then you can kill the old contract, after the new contract correctly talks to the old datastore.

Look at our codebase to understand how we have implemented upgradable contract - https://goo.gl/p5zGEv

Note: in above GitHub repo, we are using three layers of contracts because of our use case. However, it is possible to make contract upgradable with two layers only.

Hope this helps.

1
  • This is useful repo. could you please share with the latest solidity version?
    – Div
    Nov 14, 2019 at 10:45
3

zos introduced a framework for us to easily implement an upgradable smart contract

PTAL: https://docs.zeppelinos.org/docs/start.html

2

Allows you to have a contract with a stable address, but fully controllable and upgradeable behavior.

https://github.com/u2/ether-router

https://github.com/ConsenSys/smart-contract-best-practices#upgrading-broken-contracts

1
  • 2
    Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.
    – q9f
    Dec 30, 2016 at 8:29
2

At Blend, we used ZeppelinOS to make our regular Ethereum smart contracts upgradable. Here is our step-by-step guide and sample code.

0
0

The real problem in upgradable smart contract is to migrate stored values from contract.

A much better way to build upgradable smart contract is to differentiate your storage and logics in different contracts.

Save all of your contract data in one smart contract which only accepts calls from your logic contract.

Keep changing the logics of your logic contract. However, you need be very visionary while defining the variables of storage contract.

Not the answer you're looking for? Browse other questions tagged or ask your own question.