Hawk High

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

Financial Model Breakdown Due to Incorrect Teacher Compensation Formula

Issue Summary

The graduateAndUpgrade function in the LevelOne contract implements a broken compensation model that incorrectly calculates teacher payments, leading to a severe violation of the project's financial invariants. The compensation formula doesn't account for multiple teachers sharing the 35% allocation, instead giving each teacher 35% of the entire bursary.

Impact - Critical

This implementation catastrophically breaks the financial structure of the system. Instead of preserving 60% of funds for the next level as specified in the invariants, the function attempts to distribute far more than 100% of the available funds when multiple teachers exist, leading to transaction failures or complete depletion of the bursary.

Likelihood - Certain (High)

This issue will manifest with absolute certainty any time the graduateAndUpgrade function is called in a scenario with more than one teacher. Even with a single teacher, the invariant requiring 60% retention is still violated.

Technical Details

The contract defines the payment structure constants:

uint256 public constant TEACHER_WAGE = 35; // 35%
uint256 public constant PRINCIPAL_WAGE = 5; // 5%
uint256 public constant PRECISION = 100;

However, the distribution logic fails to properly implement the intended model:

uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
// This calculates 35% of the bursary for EACH teacher, not to be shared among all teachers
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
// This pays 35% of the bursary to EACH teacher
}

Mathematical verification:

  1. Let's denote the total bursary as B

  2. Principal receives: 5% × B = 0.05B

  3. Each teacher receives: 35% × B = 0.35B

  4. With N teachers, total teacher payout: N × 0.35B = 0.35NB

  5. Total payout: 0.05B + 0.35NB = B(0.05 + 0.35N)

  6. Remaining for next level: B - B(0.05 + 0.35N) = B(1 - 0.05 - 0.35N)

For the 60% retention invariant to hold: B(1 - 0.05 - 0.35N) ≥ 0.6B 1 - 0.05 - 0.35N ≥ 0.6 0.95 - 0.35N ≥ 0.6 0.35N ≤ 0.35 N ≤ 1

Therefore, the invariant is violated whenever there is more than 1 teacher. With just 2 teachers, the function attempts to distribute 75% to teachers plus 5% to the principal (80% total), leaving only 20% instead of the required 60%.

Proof of Concept

Consider a scenario with:

  • Bursary: 10,000 USDC

  • Number of teachers: 3

Current implementation would:

  1. Pay each teacher: 10,000 × 35% = 3,500 USDC

  2. Total teacher payment: 3 × 3,500 = 10,500 USDC (already exceeding the bursary)

  3. Principal payment: 10,000 × 5% = 500 USDC

  4. Total attempted payout: 11,000 USDC (110% of available funds)

  5. Remaining for next level: -1,000 USDC (impossible)

Recommendation

Modify the payment calculation to properly divide the 35% allocation among all teachers:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// ... existing validations ...
uint256 totalTeacherPool = (bursary * TEACHER_WAGE) / PRECISION; // 35% of total
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION; // 5% of total
// The amount that should remain in the bursary after payments (60%)
uint256 remainingBursary = bursary - totalTeacherPool - principalPay;
// Calculate individual teacher payment
uint256 payPerTeacher = totalTeachers > 0 ? totalTeacherPool / totalTeachers : 0;
_authorizeUpgrade(_levelTwo);
// Pay each teacher their share of the 35% pool
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
// Pay the principal
usdc.safeTransfer(principal, principalPay);
// The remaining 60% stays in the contract for the next level
// No action needed as we're only transferring out 40%
emit BursaryInformation(totalTeacherPool, principalPay, remainingBursary);
}

This implementation ensures the correct distribution of funds according to the specified invariants, properly sharing the 35% allocation among all teachers and maintaining the 60% retention for the next level.

Updates

Lead Judging Commences

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

Give us feedback!