Problem with UUPS Proxy Upgrade: UUPSUnauthorizedCallContext Error When Using `upgradeToAndCall` in OpenZeppelin 5.0.0

Hello, I'm facing an issue with a UUPS proxy contract and need help to resolve it. Recently, I tried to upgrade to a new implementation contract using the upgradeTo method from OpenZeppelin version 5.0.0, which now only uses upgradeToAndCall.

However, I'm having difficulties when calling upgradeToAndCall, both on the proxy and the implementation contract. I am the owner of the contract, but when passing the address of the new contract and data as 0x, I receive the following error:

The transaction has been reverted to the initial state.
Error provided by the contract:
UUPSUnauthorizedCallContext
Parameters: {}

I need guidance on possible causes for this error or if there are any prerequisites I should follow before executing upgradeToAndCall.

I am using the following OpenZeppelin contracts:

  • @openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol
  • @openzeppelin/contracts-upgradeable/utils/math/SignedSafeMathUpgradeable.sol
  • @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
  • @openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol
  • @openzeppelin/contracts/token/ERC20/IERC20.sol

I appreciate any help or suggestions in advance!

Just to clarify, you are upgrading an implementation that uses OpenZeppelin Contracts version 5.0.0, to new version of your implementation that also uses OpenZeppelin Contracts version 5.0.0?

To avoid your error, ensure you are calling upgradeToAndCall on the proxy's address.

You can see that upgradeToAndCall has an onlyProxy modifier which calls _checkProxy, which ensures it is being called through a proxy, otherwise it reverts with UUPSUnauthorizedCallContext.

If you are still having the issue, please share your addresses, what network you are using, and your implementation contracts' source code (both old and new versions) if possible.

2 Likes

Ericglau, thank you for your response. I'll clarify further: Recently, I made an update to an implementation that previously used OpenZeppelin contracts in versions 4.5.0 and 4.6.0, updating it to version 5.0.0. However, when trying to use the upgradeToAndCall command on the proxy address, I encountered a gas estimation error, which prevents the execution of the said command.

Furthermore, I am having difficulties in executing any other functions through the proxy, as the same gas estimation error persists. Currently, I can only execute functions on the most recent implementation, which is version 5.0.0.

It's worth noting that the error 'UUPSUnauthorizedCallContext' appears when trying to call the upgradeToAndCall function in the contract of the latest implementation (5.0.0), and not on the proxy. After I updated the proxy with the implementation that used the v5 contracts, I couldn't call any functions on this proxy anymore, but only on the v5 implementation.

I am using the BscScan Testnet network.

Here is the address of the old implementation (contracts 4.5.0 and 4.6.0): 0x3C1EEDb833D2a7C13672Acbc0CF94D31A979192B

Proxy address:
0x231a4D18986C259815D7E20E02281Cee4DA6EE4c

Address of the latest implementation (5.0.0):
0x91e9B5e98DeAB7f707b7Fef47128b9bfB6F25b61

The contract of the latest implementation (5.0.0) is not verified, but it can be accessed through the following GitLab link:

The contracts of the latest implementation were deployed using @import, while the previous implementation that used contracts 4.5.0 and 4.6.0 was done with ./.

I appreciate your assistance in advance.

I'm pretty sure that this is not supported (i.e., your contract is not guaranteed to work as desired after the upgrade).

Oh, it is actually stated in an answer given by the same user who has replied to this question earlier on.

2 Likes

@ericglau @barakman ,

Thank you for your valuable response and for highlighting the potential issues with the upgrade path I undertook. I fully understand the concerns regarding compatibility and support for upgrading from OpenZeppelin versions 4.5.0/4.6.0 to 5.0.0.

Given the situation, I would like to know if there is any guidance or alternative solutions that you or the community might suggest. Specifically, I'm wondering if there's a way to safely revert to a previous version, or to a state that reverses this 'conflict' in the versions, or if there are other best practices for such scenarios.

I am more than willing to adapt my approach based on your recommendations and look forward to any suggestions you may provide.

Thank you once again for your time and help.

1 Like

The only possible recovery that I can think of might be to call your initialize function via the proxy to set yourself as the owner in the 5.0.0 storage layout (otherwise the owner is the zero address due to the different storage layouts). Then call upgradeToAndCall to revert back to the old implementation, which will go back to the 4.x.x storage layout. However, this might not work or cause some fields to be rewritten if your initializer also writes to your own storage.

But aside from the above possibility, it is likely your proxy is broken and unrecoverable.

To avoid these kinds of issues, I'd recommend the following:

  • Do not upgrade existing deployments between major versions of OpenZeppelin Contracts
  • Use the Upgrades Plugins which helps to validate storage layout compatibility
  • Test your upgrades on development networks first
  • Test your upgrades on testnets (which you have done, which is good!) and ensure everything looks proper before proceeding to mainnet
1 Like