Hawk High

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

Incorrect Residual Bursary Handling

Description: After distributing 5% to the principal and 35% to teachers, the remaining 60% of the bursary should persist in the 'bursary' state variable so it can be carried over or audited. The current implementation computes only the two payout shares and never recalculates or stores the residual.

Impact: Funds corresponding to the 60% residual go untracked:

  • The bursary state variable remains stale (usually still its original value or zero), breaking the invariant

principalPay + teacherTotal + residual == bursary
  • Over time, "lost" bursary accumulates invisibly, preventing accurate accounting or reuse in future sessions.

Proof of Concept:

Note: this PoC assumes that the 'Incorrect Teacher-Pay Calculation' issue has already been fixed, so that graduateAndUpgrade gets as far as splitting by totalTeachers instead of reverting earlier.

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
uint256 totalTeachers = listOfTeachers.length;
+ uint256 totalTeacherShare = (bursary * TEACHER_WAGE) / PRECISION;
+ uint256 payPerTeacher = totalTeacherShare / totalTeachers;
- uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
}

After this fix include the following test in the LevelOneAndGraduateTest.t.sol file:

function testResidualNotStored() public schoolInSession {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, "");
// Expected residual = 60% of 30e18 = 18e18
uint256 expectedResidual = (30e18 * 60) / 100;
// But bursary() remains its prior value (or zero), not the expected residual
assertTrue(levelOneProxy.bursary() != expectedResidual, "Residual 60% should be stored in bursary");
}

Recommended Mitigation: After executing all transfers, compute and persist the 60% residual explicitly:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
+ uint256 residual = bursary - principalPay - (payPerTeacher * listOfTeachers.length);
+ bursary = residual;
}
Updates

Lead Judging Commences

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

bursary not updated

The bursary is not updated after wages have been paid in `graduateAndUpgrade()` function

Support

FAQs

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