Due to an error in calculating the actor teacher's fee, when there are two or more teachers in the system, paying their salaries will deplete the bursary in system.
pragma solidity 0.8.26;
import {Test, console2} from "forge-std/Test.sol";
import {DeployLevelOne} from "../script/DeployLevelOne.s.sol";
import {GraduateToLevelTwo} from "../script/GraduateToLevelTwo.s.sol";
import {LevelOne} from "../src/LevelOne.sol";
import {LevelTwo} from "../src/LevelTwo.sol";
import {MockUSDC} from "./mocks/MockUSDC.sol";
contract LevelOneAndGraduateTest is Test {
DeployLevelOne deployBot;
GraduateToLevelTwo graduateBot;
LevelOne levelOneProxy;
LevelTwo levelTwoImplementation;
address proxyAddress;
address levelOneImplementationAddress;
address levelTwoImplementationAddress;
MockUSDC usdc;
address principal;
uint256 schoolFees;
address alice;
address bob;
address clara;
address dan;
address eli;
address fin;
address grey;
address harriet;
function setUp() public {
deployBot = new DeployLevelOne();
proxyAddress = deployBot.deployLevelOne();
levelOneProxy = LevelOne(proxyAddress);
usdc = deployBot.getUSDC();
principal = deployBot.principal();
schoolFees = deployBot.getSchoolFees();
levelOneImplementationAddress = deployBot.getImplementationAddress();
alice = makeAddr("first_teacher");
bob = makeAddr("second_teacher");
clara = makeAddr("first_student");
dan = makeAddr("second_student");
eli = makeAddr("third_student");
fin = makeAddr("fourth_student");
grey = makeAddr("fifth_student");
harriet = makeAddr("six_student");
usdc.mint(clara, schoolFees);
usdc.mint(dan, schoolFees);
usdc.mint(eli, schoolFees);
usdc.mint(fin, schoolFees);
usdc.mint(grey, schoolFees);
usdc.mint(harriet, schoolFees);
}
function test_poc_teacher_payment() external {
_teachersAdded();
_studentsEnrolled();
uint256 totalBursary = levelOneProxy.bursary();
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
assertEq(usdc.balanceOf(principal), (totalBursary * 5) / 100);
assertEq(usdc.balanceOf(address(levelOneProxy)), (totalBursary * 60) / 100);
}
function _teachersAdded() internal {
vm.startPrank(principal);
levelOneProxy.addTeacher(alice);
levelOneProxy.addTeacher(bob);
vm.stopPrank();
}
function _studentsEnrolled() internal {
vm.startPrank(clara);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(dan);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(eli);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(fin);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(grey);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(harriet);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
}
}
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [988] ERC1967Proxy::fallback() [staticcall]
│ ├─ [514] LevelOne::bursary() [delegatecall]
│ │ └─ ← [Return] 30000000000000000000000 [3e22]
│ └─ ← [Return] 30000000000000000000000 [3e22]
├─ [445078] → new LevelTwo@0x2e234DAe75C793f67A35089C9d99245E1C58470b
│ └─ ← [Return] 2223 bytes of code
├─ [0] VM::prank(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca])
│ └─ ← [Return]
├─ [84129] ERC1967Proxy::fallback(LevelTwo: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 0xd3618cca)
│ ├─ [83640] LevelOne::graduateAndUpgrade(LevelTwo: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 0xd3618cca) [delegatecall]
│ │ ├─ [25750] MockUSDC::transfer(first_teacher: [0xeeEeC5A3afd714e3C63A0b1ef6d80722Bcc514b3], 10500000000000000000000 [1.05e22])
│ │ │ ├─ emit Transfer(from: ERC1967Proxy: [0x90193C961A926261B756D1E5bb255e67ff9498A1], to: first_teacher: [0xeeEeC5A3afd714e3C63A0b1ef6d80722Bcc514b3], value: 10500000000000000000000 [1.05e22])
│ │ │ └─ ← [Return] true
│ │ ├─ [25750] MockUSDC::transfer(second_teacher: [0xb4c265c1f1d07474E3715F65724E8fa9d662BF0e], 10500000000000000000000 [1.05e22])
│ │ │ ├─ emit Transfer(from: ERC1967Proxy: [0x90193C961A926261B756D1E5bb255e67ff9498A1], to: second_teacher: [0xb4c265c1f1d07474E3715F65724E8fa9d662BF0e], value: 10500000000000000000000 [1.05e22])
│ │ │ └─ ← [Return] true
│ │ ├─ [25750] MockUSDC::transfer(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], 1500000000000000000000 [1.5e21])
│ │ │ ├─ emit Transfer(from: ERC1967Proxy: [0x90193C961A926261B756D1E5bb255e67ff9498A1], to: principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca], value: 1500000000000000000000 [1.5e21])
│ │ │ └─ ← [Return] true
│ │ └─ ← [Stop]
│ └─ ← [Return]
├─ [851] MockUSDC::balanceOf(principal: [0x6b9470599cb23a06988C6332ABE964d6608A50ca]) [staticcall]
│ └─ ← [Return] 1500000000000000000000 [1.5e21]
├─ [0] VM::assertEq(1500000000000000000000 [1.5e21], 1500000000000000000000 [1.5e21]) [staticcall]
│ └─ ← [Return]
├─ [851] MockUSDC::balanceOf(ERC1967Proxy: [0x90193C961A926261B756D1E5bb255e67ff9498A1]) [staticcall]
│ └─ ← [Return] 7500000000000000000000 [7.5e21]
├─ [0] VM::assertEq(7500000000000000000000 [7.5e21], 18000000000000000000000 [1.8e22]) [staticcall]
│ └─ ← [Revert] assertion failed: 7500000000000000000000 != 18000000000000000000000
└─ ← [Revert] assertion failed: 7500000000000000000000 != 18000000000000000000000
Manual.