Hawk High

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

The total amount of bursary stored in LevelTwo::bursary variable after upgrade doesn't reflect the correct total amount.Actual Bursary fees left is less than 60% and this breaks the protocol invariant

Summary

The protocol stores all bursary fees in LevelOne::bursary variable upon each enrolment of students, however when the principal calls LevelOne::graduateAndUpgrade and payments are been made, the LevelOne::bursary variable is never updated and this reflects an inaccurate amount in the protocol. In real sense the protocol has less than 60% of the initial bursary fees and this breaks one of the invariants

Vulnerability Details

The function does not decrement the LevelOne::bursary variable which stores all schoolFees in LevelOne::graduateAndUpgrade, instead it transfers shares to beneficiaries(principal and teachers) directly without no virtual balance update for LevelOne::bursary. Secondly the remaining bursary does not amount to 60% bursary left after transfer of tokens

Using the foundry testing library , include this in the test suite

function testBursaryIsCorrect() public schoolInSession{
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(usdc.balanceOf(address(levelTwoProxy)), "Actual balance left");
console2.log(levelTwoProxy.bursary(), "Level Two Bursary variable");
//Expected Result
// Principal Share --- 5/100 * 30000e18(total busary) = 1500e18
// Given 35% is to be shared equally among 2 teachers(Alice and Bob)
// uint256 totalTeachersAmount = (totalFunds * 35) / 100;
// uint256 perTeacherAmount = totalTeachersAmount / 2;
//Each Teachers Share should be ---- 10500 / 2 which amounts to 5250e18 usdc per teacher
// The principal Share should be calculated as 1500e18 (5% of 30000e18)
//The real bursary amount left after upgrade is 7500e18 usdc (25% of total bursary instead of 60% according to the invariant)
}
Ran 1 test for test/LeveOnelAndGraduateTest.t.sol:LevelOneAndGraduateTest
[PASS] testBursaryIsInCorrect() (gas: 1419469)
Logs:
7500000000000000000000 Actual balance left
30000000000000000000000 Level Two Bursary variable

Impact

This clearly breaks the protocol invariant and could deceive the protocol to think they have more bursary than they actually do by reading the LevelTwo::bursary storage variable

Tools Used

Manual Review and Foundry Testing

Recommendations

One of the solutions the protocol can use is to refactor the LevelOne::graduateAndUpgrade function to do this below, after upgrade, the LevelTwo::bursary variable will reflect the correct percentage of total bursary left in the contract

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;
+ uint256 amountToBeSharedByTeachers = (bursary * 35) / 100;
+ uint256 payPerTeacher = amountToBeSharedByTeachers / 2;
+ bursary -= payPerTeacher * totalTeachers;
+ bursary -= principalPay;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}
Updates

Lead Judging Commences

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

Give us feedback!