Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Smart Contract Audit Report

Summary

This report reviews the security of two smart contracts, LevelOne and LevelTwo, designed for a decentralized learning environment with USDC-based payments, role-based access (principal, teachers, students), and upgradeable functionality. Both contracts are structured with OpenZeppelin's upgradeable framework.

While the structure is modular and extensible, the current implementations reveal critical vulnerabilities and incomplete logic that may lead to contract takeover, misuse of roles, inconsistent data states, and broken payment mechanics.

Vulnerability Details

1. Uninitialized Upgradeable Contracts (Both Contracts)

Type: Critical
Location: LevelOne.sol, LevelTwo.sol
Description: Neither contract contains an initialize() function to set initial values such as principal, usdc, or role assignments. In an upgradeable (proxy-based) context, this exposes a contract to untrusted initialization (SWC-118).
Consequence: An attacker can initialize the contract and assume complete control, including claiming roles, setting token contracts, or draining funds.

2. Public graduate() Function Without Access Control (LevelTwo.sol)

Type: High
Location: LevelTwo.sol
Description: The graduate() function is publicly callable and only protected by reinitializer(2). Without access control, it becomes a latent threat if additional upgrade logic is introduced.
Consequence: Unauthorized invocation of logic in future upgrades or reinitialization attacks.

3. Duplicate Role Entries (LevelOne.sol, LevelTwo.sol)

Type: Medium
Location: listOfTeachers, listOfStudents arrays
Description: When roles are assigned, no checks prevent re-adding the same address to the arrays, even if isTeacher or isStudent is already true.
Consequence: Role inflation. If used for wage distribution or iterations, duplicates can lead to overpayments or incorrect behavior.

4. No ERC20 Handling or Payment Logic (LevelTwo.sol)

Type: Medium
Description: Despite including SafeERC20, a bursary, and wage constants (TEACHER_WAGE_L2, PRINCIPAL_WAGE_L2), there are no functions to deposit, withdraw, or transfer USDC to teachers or the principal.
Consequence: Token logic is non-functional; bursary cannot be utilized or distributed securely.

5. Unused/Inert State Variables (LevelTwo.sol)

Type: Low
Variables: inSession, sessionEnd, cutOffScore, studentScore
Description: These variables exist without functional use in the current version.
Consequence: Misleading or dead code. If relied on by future features, this can lead to broken behavior.

6. Hardcoded Precision Constant (LevelTwo.sol)

Type: Low
Description: A PRECISION constant of 100 is defined, which may not align with the USDC tokenโ€™s 6 decimal precision or other ERC20 tokens.
Consequence: Calculation inaccuracies during financial distribution.

Impact

Vulnerability Severity Impact Summary
Uninitialized contracts ๐Ÿ”ด Critical Total contract takeover by attacker
Public graduate() ๐Ÿ”ด High Uncontrolled access to future upgrades
Duplicate role entries ๐ŸŸ  Medium Role inflation and state inconsistency
No USDC handling ๐ŸŸ  Medium Broken bursary and payment mechanics
Unused session variables ๐ŸŸก Low Unmaintainable code; future bugs
Fixed precision mismatch ๐ŸŸก Low Financial miscalculations

Tools Used

  • Manual Code Review

  • Slither (static analysis)

  • VSCode Solidity Plugin

  • OpenZeppelin Contracts Reference

  • SWC Registry

Recommendations

1. Implement Secure Initialization (Both Contracts)

Add an initialize() method to properly set principal, token addresses, and initial roles:

solidity
function initialize(address _usdc, address _principal) public initializer {require(_usdc != address(0), "Invalid token address");
require(_principal != address(0), "Invalid principal");
usdc = IERC20(_usdc);
principal = _principal;
}

2. Restrict graduate() to Authorized Roles

solidity
modifier onlyPrincipal() {require(msg.sender == principal, "Access Denied");
_; }
function graduate() public reinitializer(2) onlyPrincipal {// Future logic }

3. Prevent Duplicate Entries in Role Arrays

When assigning new teachers/students:

solidity
if (!isTeacher[_teacher])
{isTeacher[_teacher] = true;
listOfTeachers.push(_teacher); }

4. Implement ERC20 Deposit and Payment Flows

Example: deposit bursary

solidity
function deposit(uint256 amount) external { usdc.safeTransferFrom(msg.sender, address(this), amount);
bursary += amount; }

And payments:

solidity
function payTeacher(address teacher) external onlyPrincipal {
require(isTeacher[teacher], "Not a teacher");
usdc.safeTransfer(teacher, TEACHER_WAGE_L2 * 10**6);
// For USDC 6 decimals
}

5. Clean or Complete Session Logic

Include mechanisms to start and end sessions:

solidity
function startSession(uint256 duration) external onlyPrincipal {
inSession = true;
sessionEnd = block.timestamp + duration;
}
function endSession() external onlyPrincipal {
require(block.timestamp >= sessionEnd, "Session ongoing");
inSession = false;
}

6. Dynamically Support Token Decimals

Instead of hardcoded precision, use:

solidity
uint8 decimals = IERC20Metadata(address(usdc)).decimals();
Updates

Lead Judging Commences

yeahchibyke Lead Judge
27 days ago
yeahchibyke Lead Judge 16 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
yeahchibyke Lead Judge 16 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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