What can be the workaround to work with the new Initializable contract?

If we are using the initializable contract before OZ 0.5.0 for our transparent Upgradeable proxy.

It had two variables in slot 0.

uint8 private _initialized;
bool private _initializing;

Now in OZ 0.5.0 the storage alignment has been changed, including a struct and namespaces storage pattern.

struct InitializableStorage {
       uint64 _initialized;
       bool _initializing;
   }

The problem is if we try to use the latest initializable contract for our new implementation contract, it messes up the storage layout because now slot 0 is empty, and rest of our contract variables points to one storage earlier than the originally set slot.

What should be the solution for this:

  1. We add a variable at the top to use up slot 0, so that rest of the variables start from their orginal slot.
  2. We use OZ 0.4.9 because it has the old initializable, that uses up the slot 0.
2 Likes

It is NOT safe to upgrade a proxy from an implementation that uses OpenZeppelin Contracts 4.x to an implementation that uses 5.x. Version 5.x is a major version and has breaking changes, including (but not limited to) the storage layout differences that you mentioned above.

If your implementation contract is on 4.x, you can update to the latest 4.x release which at the moment is 4.9.6.

2 Likes

Thanks for the reply.

I also think that using 4.9 should be the way.

Actually the current contracts are using 3.4, not event version 4.

Can you also point out any other similar changes in the storage layouts or any type of concerning changes?

As I already have tested with 4.9, and it seems to be working fine. Just wanna confirm if there are any more chances of hidden bugs.

Upgrading from 3.x to 4.x is not officially supported, and the release notes for v4.0.0 state Note: Upgradeable contracts using OpenZeppelin Contracts 3.x cannot upgrade to 4.x. However, if you still want to do so, ensure you are aware of all the changes, use the Upgrades Plugins to help validate the upgrade for storage layout compatibility, and test it thoroughly on testnets/forked networks.

Alternatively, consider looking into a way to migrate usage of your existing proxy to a new proxy with an implementation using OpenZeppelin Contracts 5.x.

2 Likes

I see.

As we were previously using solidity <0.7.0 and now we are planning to use >0.8.0, hence OZ 4.x is being chosen.

I also went through the changes once, couldn't find any change that can break our contracts in the future.
For reference, these are the libraries, contracts and interfaces we are using from OZ:

Strings.sol
SafeMath.sol
Initializable.sol
Address.sol
IERC20.sol
SafeERC20.sol
PausableUpgradeable.sol

Moving ahead, safeMath would no longer be in use, and the interfaces don't have issues.
The libraries should work as expected.
The only concerning part is PausableUpgradeable.sol and Initializable.sol which doesn't include any breaking change, apart from removal of _isConstructor check in intializable contract.

EDIT: I initially checked 4.0 only, but 4.9 is also a bit different from 4.0. the bool has been changed to uint8.

So, there shouldn't be any problem. What do you think?

Thank you for the responses.

I think what you said makes sense, but again you should review and test thoroughly. uint8 and bool are the same in terms of storage layout.

1 Like