Is upgrading contracts npm package safe for upgradeable contracts?

Hi everyone,

Recently we wrote a contract using an Upgradeable Proxy pattern (EIP173) similar to the Transparent Proxy pattern used by OpenZeppelin.

Notably we did not use the OZ SDK or CLI.

At one point we changed solidity compiler versions from 0.8 to 0.7 and downgraded the @openzeppelin/contracts package from 4.1.0 to 3.4.0 .

Upon upgrading our Proxy thereafter, we noticed that the state storage values were corrupted after the upgrade. However, we hadnt changed the contracts inheritance or state layout.

The contract inherits state from OZ contracts like:

  • ERC20Permit

  • Initializable

  • ReentrancyGuard

We wondered whether downgrade in the @openzepplin/contracts version might have resulted in some of the contracts dependencies to have a different state layout than before and thus break the safety of the upgradeable layout we relied on?

Question: when using upgradeable contracts that have to stick to an append-only state layout, is it unsafe to switch versions of the @openzeppelin/contracts package in between upgrades?

Is there a granular safe way to switch between versions of the package? E.g. everything within a major version is safe for upgrading, whereas switching in between major versions of the package is dangerous because state layout might be changed in between major versions?

1 Like

Yes, changing the major version of OpenZeppelin Contracts was definitely the culprit here. Storage layout is not preserved across major versions.

As seen in the readme for OpenZeppelin Contracts Upgradeable:

:warning: Warning

There will be storage incompatibilities across major versions of this package, which makes it unsafe to upgrade a deployed contract from one major version to another, for example from 3.4.0 to 4.0.0.

Similarly, it is not safe to upgrade from @openzeppelin/contracts-ethereum-package (a similar previous package) to @openzeppelin/contracts-upgradeable.

It is strongly encouraged to use these contracts together with a tool that can automatically guarantee the safety of an upgradeable contract, such as the OpenZeppelin Upgrades Plugins.

Two things you should note here:

  1. You need to use @openzeppelin/contracts-upgradeable for upgradeable contracts or any kind of proxy deployment (e.g. minimal proxy clones).
  2. You should use the Upgrades Plugins for Hardhat and Truffle, which perform important security checks including storage layout compatibility.

Lastly, it’s not clear to me if this error happened in production or during testing. But always test your upgrades before doing them in production.

1 Like