Hawk High

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

Failure to Transfer 60% of `bursary` to `LevelTwo` After Upgrade

Summary

The graduateAndUpgrade function in LevelOne.sol and the LevelTwo.sol contract lack mechanisms to transfer or initialize the remaining 60% of the bursary after the system upgrade, violating the project's invariant that 60% of the bursary should be reflected in the new contract post-upgrade.

Vulnerability Details

According to the project invariants, after paying 5% to the principal and 35% to teachers, the remaining 60% of the bursary should be available in the LevelTwo contract after the system upgrade. However:

  • In LevelOne.sol, the graduateAndUpgrade function does not include logic to transfer the remaining 60% of the bursary (in USDC tokens) to the proxy or LevelTwo contract.

  • In LevelTwo.sol, the graduate function (with reinitializer(2)) does not initialize the bursary variable with the remaining funds, leaving it at its default value of zero.

Relevant code in graduateAndUpgrade:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}

No logic transfers the remaining 60% of the bursary. In LevelTwo.sol, the bursary variable is not initialized:

function graduate() public reinitializer(2) {}

Impact

  • Impact: High
    This vulnerability directly puts funds at risk, as the remaining 60% of the bursary is not transferred or accessible in LevelTwo, effectively locking or losing a significant portion of the protocol's funds. This renders the upgraded system financially non-functional, as LevelTwo starts with a bursary of zero, severely disrupting protocol operations.

  • Likelihood: High
    The issue is automatic and inevitable during the upgrade process, as the lack of transfer or initialization logic is embedded in the contracts. It occurs every time graduateAndUpgrade is called, requiring no malicious intent.

Tools Used

  • Manual code analysis

Recommendations

Implement logic to transfer and initialize the remaining 60% of the bursary in LevelTwo. Suggested modifications:

  1. In LevelOne.sol, calculate and transfer the remaining 60% to the proxy or LevelTwo:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = totalTeachers > 0 ? (bursary * TEACHER_WAGE) / PRECISION / totalTeachers : 0;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
uint256 remainingBursary = (bursary * 60) / PRECISION; // 60% of bursary
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
usdc.safeTransfer(_levelTwo, remainingBursary); // Transfer remaining 60%
}
Updates

Lead Judging Commences

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

stuck funds in system

Funds are stuck in `LevelOne()` contract after upgrade.

Support

FAQs

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