Unable to read state variables in _authorizeUpgrade - Error: Address: low-level delegate call failed

function _authorizeUpgrade(address newImplementation)
            hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
        require(approvedProposalsExpiry[newImplementation] > block.timestamp);

Can you share the code where you call upgradeTo?

You need to call this function on the proxy, not on the implementation contract.

BurgerToken = await ethers.getContractFactory('Burger', {
    libraries: {
        BurgerERC20: BurgerERC20.address,

Burger = await upgrades.deployProxy(BurgerToken, [eoaAddress], {
    kind: 'uups',
    unsafeAllowLinkedLibraries: true

  const BurgerTokenV2 = await ethers.getContractFactory('BurgerV2', {
    libraries: {
      BurgerERC20: BurgerERC20.address,

await upgrades.upgradeProxy(Burger.address, BurgerTokenV2, {
    unsafeAllowLinkedLibraries: true

Everything looks normal and like it should work. Can you share your full code in a repository so I can try to reproduce it?

perhaps a tests case in github to test upgrade of proxy at a given contract address that got deployed earlier, then deploy new implementation using prepareUpgrade followed by upgrading it with address of new implementation would help. I’ll try to make a PR if someone can show pointers on how to call upgradeTo directly via upgrades library instead upgrades.upgradeProxy

@frangio I tried by directly calling the public .upgradeTo(newImpl) function with implementation address and got the same error Address: low-level delegate call failed

this fails exactly at rollback testing _functionDelegateCall. any pointers would be helpful:

// Perform rollback test if not already in progress
        StorageSlotUpgradeable.BooleanSlot storage rollbackTesting = StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT);
        if (!rollbackTesting.value) {
            // Trigger rollback using upgradeTo from the new implementation
            rollbackTesting.value = true;
            rollbackTesting.value = false;
            // Check rollback was effective
            require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
            // Finally reset to the new implementation and log the upgrade
            emit Upgraded(newImplementation);

Note that my ImplementationV2 only inherits from ImplementationV1 and ImplementationV2 doesn’t explicitly add an upgradeTo function. could this be an issue? I assume the upgradeTo is inherited to ImplementationV2 as well.

What I understand is that the rollback test is trying to upgrade back to the old implementation which will try to call the _authorizeUpgrade function but then my _authorizeUpgrade function has a custom logic based on DAO which will revert only unless voting accepts the current implementation as well.

I’m going to write tests to simulate this behavior in which case I assume my last option would be to make voting such that _authorizeUpgrade gets called without reverting for both current implementation address as well as for the new implementation address.

EDIT 2: Yes. my tests are passing if DAO votes for both the current implementation address as well as the new implementation address.

EDIT 3: Solution from here for me would be to change _authorizeUpgrade such that it allows without reverting for current implementation address as well and get the upgrade done at once.

Oh that’s really interesting. Yes your _authorizeUpgrade will need to accomodate the rollback test.

One of the things you can do is retrieve from storage a boolean stating whether the contract is ongoing a rollback test. See here to get an idea of how it works:

You could also get rid of the rollback test, understanding that there is some risk in doing so. In order to do this, you would have to inherit ERC1967Upgrade and define your own upgradeTo public function, by using the internal _upgradeTo function.