function _authorizeUpgrade(address newImplementation)
internal
override
view
{
require(
hasRole(DEFAULT_ADMIN_ROLE, msg.sender));
require(approvedProposalsExpiry[newImplementation] > block.timestamp);
}
Unable to read state variables in _authorizeUpgrade - Error: Address: low-level delegate call failed
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;
_functionDelegateCall(
newImplementation,
abi.encodeWithSignature(
"upgradeTo(address)",
oldImplementation
)
);
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
_setImplementation(newImplementation);
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.
EDIT:
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.