Description
The LevelOne::graduateAndUpgrade
function incorrectly calculates payPerTeacher
by applying the bursary and TEACHER_WAGE
factor without distributing the result among the number of teachers. This miscalculation causes incorrect fund allocation, either overpaying or underpaying teachers depending on their count.
Vulnerable Function
function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
...
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
}
Impact
-
Financial Risk: Teachers may receive more or less than the correct share of the bursary.
-
Overdraft: The contract may transfer more than it holds or intends to pay.
-
Unfair Distribution: Rewards are misaligned with teacher contribution and count.
-
Bursary Drain: Future levels may receive reduced funding due to overpayment.
Proof of Concept
function test_confirm_OnwerCanCompleteSessionBeforeTime() public schoolInSession {
vm.warp(block.timestamp + 1 weeks);
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
LevelTwo levelTwoProxy = LevelTwo(proxyAddress);
console2.log(IERC20(usdc).balanceOf(address(alice)));
console2.log(IERC20(usdc).balanceOf(address(bob)));
console2.log(IERC20(address(usdc)).balanceOf(address(proxyAddress)));
}
Recommendation
Correct the calculation by dividing the total teacher wages by the number of teachers:
uint256 totalTeachers = listOfTeachers.length;
require(totalTeachers > 0, "HH: No teachers registered");
uint256 totalTeacherWages = (bursary * TEACHER_WAGE) / PRECISION;
uint256 payPerTeacher = totalTeacherWages / totalTeachers;
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}