Hawk High

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

`bursary` Is Never Decreased After Funds Are Distributed

Description:

In the graduateAndUpgrade function, the contract correctly calculates and transfers USDC to both the principal and the teachers. However, it fails to update the bursary state variable to reflect the distributed funds. This means the internal accounting of the contract is incorrect: the bursary still shows 100% of the funds available, even though a large portion has already been paid out.

uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
// Payments are made...
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
usdc.safeTransfer(principal, principalPay);
// bursary is never updated here

Impact:

  • Future calculations relying on the bursary value (e.g., further distributions or upgrades) will be completely inaccurate.

  • The contract may attempt to pay out more funds than it actually has, resulting in reverts or unexpected behavior.

  • It creates a false sense of available funds, which can be exploited or cause system malfunction in upgrades or dependent modules.

Proof of Concept:

A test that logs bursary before and after graduateAndUpgrade shows that the variable still unchanged.

function test_bursary_percentage_is_good() public schoolInSession {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
uint256 initialBursary = levelOneProxy.bursary();
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
uint256 finalBursary = levelOneProxy.bursary();
// Calculates how much % of the initial bursary is left after payments
uint256 actualLeftPercent = (finalBursary * 100) / initialBursary;
console2.log("Bursary remaining:", finalBursary);
console2.log("Bursary initial:", initialBursary);
console2.log("Bursary remaining percentage:", actualLeftPercent);
// Verify the remainder percentage
assertLt(actualLeftPercent, 60, "Bursary remaining is not less than 60%");
}
├─ [0] console::log("Bursary remaining:", 30000000000000000000000 [3e22]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Bursary initial:", 30000000000000000000000 [3e22]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Bursary remaining percentage:", 100) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::assertLt(100, 60, "Bursary remaining is not less than 60%") [staticcall]
│ └─ ← [Revert] Bursary remaining is not less than 60%: 100 >= 60
└─ ← [Revert] Bursary remaining is not less than 60%: 100 >= 60

The logs are showing that the bursary starts at 3e22 and finish at 3e22.

Recommended Mitigation:

Decrease bursary after payments are made:

uint256 totalPaid = (payPerTeacher * totalTeachers) + principalPay;
require(bursary >= totalPaid, "Not enough bursary");
bursary -= totalPaid;

Severity: High (Incorrect internal accounting leading to systemic financial inconsistency)

Tools Used

  • Manual Review

Updates

Lead Judging Commences

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