Hawk High

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

Incorrect Payout Logic in `graduateAndUpgrade` Leads to Excessive Fund Distribution or Revert

Summary

The graduateAndUpgrade function in the LevelOne contract incorrectly calculates teacher payouts during the graduation process. It calculates the total amount intended for all teachers (35% of the bursary) but then attempts to transfer this full amount to each individual teacher in a loop. This results in the contract attempting to pay out significantly more funds than allocated (numberOfTeachers * 35% of the bursary), which will likely cause the transaction to fail due to insufficient balance or, in rare cases of overfunding, drain the contract's bursary excessively.

Vulnerability Details

Background:
The LevelOne contract manages student enrollment, school sessions, and teacher roles. Students pay schoolFees in USDC, which accumulate in the contract's bursary. The graduateAndUpgrade function is intended to be called by the principal at the end of a cycle to distribute a portion of the bursary as wages (35% total to teachers, 5% to the principal) and authorize an upgrade to a LevelTwo contract.

Root Cause:
The vulnerability lies in the calculation and distribution logic for teacher wages within the graduateAndUpgrade function.

  1. The variable payPerTeacher is calculated as (bursary * TEACHER_WAGE) / PRECISION. Given TEACHER_WAGE = 35 and PRECISION = 100, this calculates 35% of the total bursary. This amount represents the total pool of funds designated for all teachers combined.

  2. However, inside the for loop that iterates through listOfTeachers, the contract executes usdc.safeTransfer(listOfTeachers[n], payPerTeacher); for each teacher. This attempts to send the entire 35% share to each teacher individually.

Triggering the Vulnerability:

  1. The principal calls the graduateAndUpgrade function.

  2. The contract calculates payPerTeacher as 35% of the current bursary balance.

  3. The loop begins iterating through the listOfTeachers array.

  4. For the first teacher (n=0), the contract attempts to transfer the full payPerTeacher amount.

  5. If there is a second teacher (n=1), the contract attempts to transfer the same full payPerTeacher amount again to the second teacher.

  6. This continues for all teachers in the list.

Code Snippet:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// ... existing checks ...
uint256 totalTeachers = listOfTeachers.length;
// INCORRECT: Calculates the TOTAL wage pool for ALL teachers (35% of bursary)
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
// INCORRECT: Loop transfers the TOTAL wage pool to EACH teacher
for (uint256 n = 0; n < totalTeachers; n++) {
// This sends the full 35% share to listOfTeachers[0], then again to listOfTeachers[1], etc.
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}

Impact

The impact depends on the number of teachers and the actual bursary balance compared to the intended payout:

Transaction Revert (Denial of Service): This is the most likely outcome if totalTeachers > 1. For example, with 2 teachers, the function attempts to pay out 35% (first teacher) + 35% (second teacher) + 5% (principal) = 75% of the bursary. If the bursary only contains the funds from schoolFees, it likely won't have enough USDC to cover the second teacher's payment after the first one is made (or even the first one if bursary is unexpectedly low). The safeTransfer call will fail due to insufficient balance, causing the entire graduateAndUpgrade transaction to revert. This prevents teachers and the principal from receiving any payment and blocks the contract upgrade process.

Excessive Fund Depletion: In the unlikely scenario where the contract's bursary holds significantly more USDC than expected (e.g., due to direct transfers or calculation errors elsewhere), the initial transfers might succeed. However, the contract will pay out far more than the intended 35% total teacher wage. With 3 teachers, it would attempt to pay 105% of the bursary just to teachers, leading to a severe drain of funds until the balance is exhausted or the transaction finally reverts. This constitutes a critical loss of funds beyond the intended economic design.

In summary, the vulnerability leads either to a denial of service preventing payouts and upgrades or a critical drain of the contract's funds allocated for salaries.

Updates

Lead Judging Commences

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

incorrect teacher pay calculation

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

Support

FAQs

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