Hawk High

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

[H-6] Missing Funds Transfer Mechanism During Upgrade

Severity

High

Impact

The protocol is designed to preserve 60% of bursary funds across the upgrade from LevelOne to LevelTwo. However, there is no mechanism to actually transfer or account for these funds during the upgrade process. This will result in:

  1. Funds effectively being stranded in the proxy contract

  2. Loss of financial accounting between versions

  3. Inability to utilize the remaining 60% of funds as intended in LevelTwo

  4. Breaking the core economic model of the school system

Description

According to the project requirements:

Payment structure is as follows:

  • principal gets 5% of bursary

  • teachers share of 35% of bursary

  • remaining 60% should reflect in the bursary after upgrade

While LevelOne's graduateAndUpgrade() function correctly distributes the 40% of funds to teachers and principal, it has no mechanism to account for the remaining 60% when upgrading to LevelTwo:

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);
}

Similarly, LevelTwo's graduate() function is empty and doesn't attempt to handle the remaining funds:

function graduate() public reinitializer(2) {}

This creates a critical disconnect between the protocol's stated requirements and its implementation. The actual USDC tokens will remain in the proxy contract's balance, but LevelTwo has no awareness of this bursary contribution, effectively "losing" 60% of the funds in the transition.

Tools Used

Manual code review

Recommended Mitigation

Implement a proper fund transfer mechanism between contracts:

  1. In LevelOne's graduateAndUpgrade() function, calculate the remaining funds and pass them to LevelTwo:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// ... existing code ...
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
+ uint256 remainingBursary = bursary - (totalTeachers * payPerTeacher) - principalPay;
+ // Create initialization data with remaining bursary amount
+ bytes memory initData = abi.encodeWithSelector(
+ LevelTwo(_levelTwo).graduate.selector,
+ remainingBursary
+ );
- _authorizeUpgrade(_levelTwo);
+ _upgradeToAndCall(_levelTwo, initData);
// ... payments to teachers and principal ...
}
  1. In LevelTwo, update the graduate() function to handle the bursary amount:

- function graduate() public reinitializer(2) {}
+ function graduate(uint256 _remainingBursary) public reinitializer(2) {
+ // Set the bursary value in LevelTwo
+ bursary = _remainingBursary;
+ }

This ensures the 60% of funds are properly accounted for in LevelTwo's state.

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 2 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.