Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Upgrade Logic Missing — `graduateAndUpgrade()` Fails to Perform Actual Upgrade

Summary

The graduateAndUpgrade() function is expected to handle contract upgrades. However, it currently only calls _authorizeUpgrade(), which merely performs an access control check. The actual upgrade operation via upgradeToAndCall() is never invoked. Additionally, any new implementation deployed through this function must be implemented proxiableUUID() as required by the UUPS pattern — otherwise, the upgrade will revert.

Vulnerability Details

In the current implementation:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
...
_authorizeUpgrade(_levelTwo); // access control only, does not perform upgrade
...
}

The _authorizeUpgrade() function is defined but left empty:

function _authorizeUpgrade(address newImplementation) internal override onlyPrincipal {
// empty
}

This is insufficient for an actual upgrade. The missing part is the UUPS upgrade call, typically:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
...
//_authorizeUpgrade(_levelTwo);
upgradeToAndCall(_levelTwo, ""); // the correct way to upgrade
...
}

Furthermore, the new implementation contract must implement the proxiableUUID() function:

// src/LevelTwo.sol::LevelTwo
function proxiableUUID() external view virtual returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}

Impact

  • The system never upgrades, leaving critical logic unchangeable.

  • Misleads contract users and stakeholders about the state of the protocol.

  • Future bug fixes, upgrades, or governance changes become impossible.

  • Without proxiableUUID(), even a correct call to upgradeToAndCall() would fail.

Tools Used

  • Manual source code review

  • Understanding of UUPSUpgradeable (EIP-1822 + ERC-1967) standards

Recommendations

  • Replace _authorizeUpgrade(_levelTwo); with:

// src/LevelOne.sol::LevelOne::graduateAndUpgrade()
upgradeToAndCall(_levelTwo, "");
  • Ensure the new implementation includes the following function:

// src/LevelTwo.sol::LevelTwo
function proxiableUUID() external view virtual returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}

This ensures compatibility with the UUPS upgrade mechanism and allows smooth execution of future upgrades.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

failed upgrade

The system doesn't implement UUPS properly.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.