Hawk High

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

Incorrect Fund Distribution Leaves Bursary Funds Locked

Summary

The graduateAndUpgrade function distributes 35% of the bursary to each teacher and 5% to the principal, leaving the remaining funds (up to 60%) locked in the contract.

Vulnerability Details

If there are two teachers, each receives 35%, totaling 70%, plus 5% to the principal—leaving 25% unallocated. The contract does not transfer the remaining USDC, causing them to be stuck indefinitely.

https://github.com/CodeHawks-Contests/2025-05-hawk-high/blob/3a7251910c31739505a8699c7a0fc1b7de2c30b5/src/LevelOne.sol#L295-L312

add this function to LevelOneAndGraduateTest:

function test_graduate_leaves_funds_locked() public schoolInSession {
uint256 initialBursary = 6 * schoolFees;
assertEq(levelOneProxy.bursary(), initialBursary, "Initial bursary mismatch");
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
// Calculate expected remaining funds: 25% of initialBursary
uint256 totalPercentageDistributed = (2 * levelOneProxy.TEACHER_WAGE()) + levelOneProxy.PRINCIPAL_WAGE();
uint256 expectedRemaining = initialBursary - (initialBursary * totalPercentageDistributed) / levelOneProxy.PRECISION();
console2.log("expectedRemaining:", expectedRemaining);
console2.log("contract balance:", usdc.balanceOf(address(levelOneProxy)));
// Check contract balance has remaining locked funds
assertEq(
usdc.balanceOf(address(levelOneProxy)),
expectedRemaining,
"Bursary funds not locked as expected"
);
}

Run test: forge test --match-test test_graduate_leaves_funds_locked -vvv

Ran 1 test for test/LeveOnelAndGraduateTest.t.sol:LevelOneAndGraduateTest
[PASS] test_graduate_leaves_funds_locked() (gas: 1427349)
Logs:
expectedRemaining: 7500000000000000000000
contract balance: 7500000000000000000000
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.75ms (1.24ms CPU time)

Assuming schoolFees = 5000 USDC and 6 students enrolled (6 × 5000 = 30000 USDC initial bursary).

This proves 25% of the bursary (7500 USDC) was left locked in the contract after distributions.

Impact

Significant portions of the school fees remain inaccessible, reducing the system's efficiency and potentially locking funds permanently.

Tools Used

Recommendations

Calculate the total distributed percentage and transfer the remaining balance to a designated address (e.g., the principal or a treasury) after distributions.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 16 days ago
Submission Judgement Published
Validated
Assigned finding tags:

incorrect teacher pay calculation

`payPerTeacher` in `graduateAndUpgrade()` is incorrectly calculated.

stuck funds in system

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

Support

FAQs

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