ICE Blockchain

Upgrading Ink! contracts

Upgrading Ink Contracts

Smart contracts are by default immutable in nature. Once uploaded to the chain, we cannot change its runtime logic or storage structure. This is an inconvenience since upgrades are common in software development. Even though the original contract is immutable, we can perform an upgrade of the contract by alternative means. Currently, there are three separate mechanisms to upgrade a contract that can be used according to the suitable use case.

1. Forwarding Calls (Proxy Pattern)

In this upgrade mechanism, we upload a new proxy contract with a mechanism to forward calls that are not implemented in the proxy contract to the actual contract. For this, we would need to set the actual contract account ID as Proxy.forward_to, for the call forwarding. Since we are setting the Contract Address to forward calls in the proxy contract, the actual contract must be deployed first.
If we deploy V2 of the contract with new methods, we can set the Proxy.forward_to parameter in the Proxy contract, with the address of the V2 contract. After that, all the calls to the Proxy contract would be directed to V2.
Migrating the storage V1 to V2 falls under the responsibility of the V1/V2 contract developers.
Example Code: Forward Call​
Forwarding Calls (Proxy Pattern)

2. Delegate Calls

This is similar to forward calls pattern. The only difference is that the unmatched calls are delegated using the upgraded contracts code hash. Since code hash can also be updated, the smart contract can be upgraded any number of times. The V1/V2 contracts does not need to be deployed (call the constructor) after uploading, since the proxy contract calls the V1/V2 contract using the code hash. The storage is lazy initialized using the Upgradeable wrapper trait.
Example Code: Delegate Call​
Delegate Calls

3. Update Runtime

This is the simplest approach to upgrading an Ink! contract. In this approach, we change the runtime execution code of a contract by changing its code hash to that of a new contract. It is basically replacing the old contract with new runtime logic. Example Code: Set Code Hash​
Update Runtime

Upgrading Storage

There is no straightforward way to upgrade existing storage in case of a change in the storage existing layout of a contract. If we need to upgrade a contract with changes in layout data structure, then the most accessible path would be to use a forward-calls pattern in which the upgraded contract initializes its own storage and imports old records from previous contract storage. Developers would need to write their own data migration procedure for this.