Contract Migration
In attacks where private keys are compromised, it may be impossible to fix the deployed smart contract, even if the contract has an upgradability mechanism. A new instance of the contract will need to be deployed and properly initialized to restore functionality to your users.
Hence, smart contract developers should integrate a migration procedure during the contract design phase.
A migration has two steps:
Recovering the data to migrate
Writing the data to the new contract
Step 1: Data Recovery
Retrieval methods by data type
Public variables - use public getter functions
Private variables - use events, or compute the location in memory of the variable, then retrieve its value using:
await provider.getStorageAt(address, slot)
Arrays - number of elements is known; use methods above
Mappings - keys are not stored, but are required to access mapping values. To simplify off-chain tracking, emit events when a value is stored in a mapping.
Holders and Balances
For token contracts, often we can find the list of all the holders by tracking the addresses of the Transfer events.
Once holder addresses are obtained, query the balanceOf function to recover the balance associated to each holder.
Step 2: Data Writing
For simple variables, set the values through the constructor of the contract.
If there is a large amount of data that cannot be sent with one transaction, the migration has to be split into several. To do this, add an initialisation state to the contract where only the owner can change the state variables, and users can’t take any action.
The initialization state can be implemented with a Pausable feature and a boolean indicating the initialization state.
To reduce the cost, the migration of the balances can be implemented with a batch transfer function that lets you set multiple accounts in a single transaction.
General Recommendations
Favoring contract migration over upgradeability. Migration system have many of the same advantages than upgradeable, without their drawbacks.
Using the data separation pattern over the delegatecallproxy one. If your project has a clear abstraction separation, upgradeability using data separation will necessitate only a few adjustments. The delegatecallproxy requires EVM expertise and is highly error-prone.
Document the migration/upgrade procedure before the deployment. If you have to react under stress without any guidelines, you will make mistakes. Write the procedure to follow ahead of time. It should include:
The calls that initiate the new contracts
Where are stored the keys and how to access them
How to check the deployment! Develop and test a post-deployment script.
References:
https://blog.trailofbits.com/2018/10/29/how-contract-migration-works/
Last updated