This is a quick sketch on what ideal contract upgrades could look like.
It is desirable to be able to roll forward and backward contract upgrades by versioning the storage layout and defining a migration between any arbitrary version. We have this concept of 2 step upgrades already where we simply zero out the initialize storage slot. We could make the 2 step upgrade a more first class citizen, where the first step of the 2 step upgrade is made smarter and knows about the storage layout schemas of the particular contract.
What this would look like in practice is breaking out the storage of a contract into its own implementation and composing together the storage contract and the implementation code. Example:
contract OptimismPortal is OptimismPortalStorage ... {}
The migration contract would look something like:
contract OptimismPortalMigration is OptimismPortalStorage {
// magic slot for the storage schema
bytes32 version slot = keccak256("opstack.storage-schema-version");
function migrate(uint256 _version) {
// replace the functionality of StorageSetter by setting the initialize slot to 0
Storage.setUint(initializableSlot, 0);
uint256 currentVersion = Storage.readUint(slot);
if (currentVersion == _version) return;
if (_version > currentVersion) {
// for each version between _version and currentVersion
// call upgradeTo(i)
}
if (_version < currentVersion) {
// for each version between currentVersion and _version
// call downgradeTo(i)
}
}
// When adding a new storage schema, the developer would add the logic here
// We would preserve all possible storage migrations
function upgradeTo(uint256 _version) internal {
//
}
}
This may not be the most simple abstractions for this idea, I think it can be improved, somebody needs to prototype this a bit more, but the idea is that we have the developer add the migration permanently to the migration contract and defines the forwards and backwards migration.
The downside of this is a new migration contract has to be deployed each time there is a new migration for a particular contract. We could have 1 migration contract that defines the transition between 2 versions or we could have the latest version always support all previous migrations, i lean towards the second.
This is a quick sketch on what ideal contract upgrades could look like.
It is desirable to be able to roll forward and backward contract upgrades by versioning the storage layout and defining a migration between any arbitrary version. We have this concept of 2 step upgrades already where we simply zero out the initialize storage slot. We could make the 2 step upgrade a more first class citizen, where the first step of the 2 step upgrade is made smarter and knows about the storage layout schemas of the particular contract.
What this would look like in practice is breaking out the storage of a contract into its own implementation and composing together the storage contract and the implementation code. Example:
The migration contract would look something like:
This may not be the most simple abstractions for this idea, I think it can be improved, somebody needs to prototype this a bit more, but the idea is that we have the developer add the migration permanently to the migration contract and defines the forwards and backwards migration.
The downside of this is a new migration contract has to be deployed each time there is a new migration for a particular contract. We could have 1 migration contract that defines the transition between 2 versions or we could have the latest version always support all previous migrations, i lean towards the second.