Hawk High

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

[M-01] Bursary Accounting Error: Contract Fails to Update Remaining Balance After Payments

Summary

The graduateAndUpgrade() function neglects to update the bursary state variable after distributing payments, causing a critical accounting mismatch where the contract's remaining funds (60% of original bursary) become inaccessible while still appearing available in storage.

Vulnerability Details

Location

  • File: levelOne.sol

  • Function: graduateAndUpgrade(address _levelTwo, bytes memory)

Critical Issues

  1. State/Reality Mismatch:

    • Pays out 40% of bursary (5% principal + 35% teachers)

    • Fails to deduct payments from bursary storage variable

    • Creates "ghost balance" where contract appears to have more funds than actually available

  2. Funds Lock Risk:

    • 60% remaining funds become effectively stuck

    • Subsequent operations using bursary will overestimate available capital

  3. Documentation Violation:

    • Explicitly contradicts protocol specs requiring "remaining 60% [to] reflect in the bursary after upgrade"

Proof of Concept

Test Case:

function test_bursary_locked_funds_accounting_error() public {
// Setup student enrollment (funds bursary)
vm.prank(clara);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
// Verify initial bursary
assertEq(levelOneProxy.getBursary(), schoolFees); // 5,000e18
// Prepare upgrade
address levelTwo = address(new LevelTwo());
vm.startPrank(principal);
levelOneProxy.addTeacher(alice);
levelOneProxy.addTeacher(bob);
// Execute payments + upgrade
levelOneProxy.graduateAndUpgrade(levelTwo, "");
vm.stopPrank();
// Bursary should be 60% of original (3,000e18) but remains unchanged
assertEq(
levelOneProxy.getBursary(),
schoolFees, // Still shows 5,000e18 (should be 3,000e18)
"Bursary not updated post-payments"
);
}

Test Result:

Ran 1 test for test/LevelOneAndGraduateTest.t.sol:LevelOneAndGraduateTest
[PASS] test_bursary_locked_funds_accounting_error() (gas: 924332)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 23.30ms (14.30ms CPU time)

Impact

High Severity because:

  • Breaks financial accounting system

  • Violates explicit payment distribution requirements

  • Could cause cascading failures in dependent functions

Tools Used

  • Foundry (forge test)

  • Protocol documentation review

Recommendations

  • Update Bursary After Payments:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// ... existing checks ...
uint256 totalPayout = principalPay + (payPerTeacher * totalTeachers);
bursary -= totalPayout; // Deduct paid amounts
// ... payment logic ...
}
Updates

Lead Judging Commences

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

stuck funds in system

Funds are stuck in `LevelOne()` contract after upgrade.

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.