diff --git a/assignments/fatiudeen/Counter.sol b/assignments/fatiudeen/Counter.sol new file mode 100644 index 00000000..86742549 --- /dev/null +++ b/assignments/fatiudeen/Counter.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +/// @title Counter Contract +/// @notice A simple contract that implements a counter with increase, decrease and reset functionality +contract Counter { + /// @notice The current value of the counter + uint public count; + + /// @notice Emitted when the counter value is increased + /// @param amount The new value of the counter + /// @param when The timestamp when the increase occurred + event CountIncreased(uint amount, uint when); + + /// @notice Emitted when the counter value is decreased + /// @param amount The new value of the counter + /// @param when The timestamp when the decrease occurred + event CountDecreased(uint amount, uint when); + + /// @notice Increases the counter by one + /// @dev Reverts if the operation would cause an overflow + function increaseByOne() public { + require(count < type(uint).max, "cannot increase beyond max uint"); + count += 1; + emit CountIncreased(count, block.timestamp); + } + + /// @notice Increases the counter by a specified value + /// @param _value The amount to increase the counter by + /// @dev Reverts if the operation would cause an overflow + function increaseByValue(uint _value) public { + require(count + _value <= type(uint).max, "cannot increase beyond max uint"); + count += _value; + emit CountIncreased(count, block.timestamp); + } + + /// @notice Decreases the counter by one + /// @dev Reverts if the operation would cause an underflow + function decreaseByOne() public { + require(count > 0, "cannot decrease below 0"); + count -= 1; + emit CountDecreased(count, block.timestamp); + } + + /// @notice Decreases the counter by a specified value + /// @param _value The amount to decrease the counter by + /// @dev Reverts if the operation would cause an underflow + function decreaseByValue(uint _value) public { + require(count >= _value, "cannot decrease below 0"); + count -= _value; + emit CountDecreased(count, block.timestamp); + } + + /// @notice Resets the counter to zero + function resetCount() public { + uint oldCount = count; + if (oldCount != 0) { + count = 0; + emit CountDecreased(count, block.timestamp); + } + } + + /// @notice Returns the current value of the counter + /// @return The current counter value + function getCount() public view returns (uint) { + return count; + } + + /// @notice Returns the maximum value of uint256 + /// @return The maximum value that can be stored in a uint256 + /// @dev Uses unchecked arithmetic to intentionally underflow and get max uint + function getMaxUint256() public pure returns (uint) { + uint max; + unchecked { + max = 0 - 1; + } + return max; + } +} diff --git a/assignments/fatiudeen/StudentRegistry.sol b/assignments/fatiudeen/StudentRegistry.sol new file mode 100644 index 00000000..fbe2ee90 --- /dev/null +++ b/assignments/fatiudeen/StudentRegistry.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +/// @title Student Registry Contract +/// @notice A contract for managing student information, attendance, and interests +contract StudentRegistry { + /// @notice Possible attendance statuses + enum Attendance { Present, Absent } + + /// @notice Structure to store student information + struct Student { + string name; + Attendance attendance; + string[] interests; + } + + /// @notice Maps student addresses to their information + mapping(address => Student) public students; + + /// @notice Contract owner address + address public owner; + + /// @notice Emitted when a new student is registered + event StudentCreated(address _studentAddress, string _name); + /// @notice Emitted when attendance is updated + event AttendanceStatus(address _studentAddress, Attendance _attendance); + /// @notice Emitted when an interest is added + event InterestAdded(address _studentAddress, string _interest); + /// @notice Emitted when an interest is removed + event InterestRemoved(address _studentAddress, string _interest); + + /// @notice Ensures only the owner can call certain functions + modifier onlyOwner() { + require(msg.sender == owner, "Only owner can call this function"); + _; + } + + /// @notice Ensures the student exists + modifier studentExists(address _address) { + require(bytes(students[_address].name).length > 0, "Student does not exist"); + _; + } + + /// @notice Ensures the student does not exist + modifier studentDoesNotExist(address _address) { + require(bytes(students[_address].name).length == 0, "Student already exists"); + _; + } + + /// @notice Sets the contract deployer as the owner + constructor() { + owner = msg.sender; + } + + /// @notice Registers a new student with full information + function registerStudent( + string memory _name, + Attendance _attendance, + string[] memory _interests + ) public studentDoesNotExist(msg.sender) { + require(bytes(_name).length > 0, "Name cannot be empty"); + require(_interests.length <= 5, "Maximum 5 interests allowed"); + + students[msg.sender] = Student({ + name: _name, + attendance: _attendance, + interests: _interests + }); + + emit StudentCreated(msg.sender, _name); + } + + /// @notice Registers a new student with just a name + function registerNewStudent(string memory _name) public studentDoesNotExist(msg.sender) { + require(bytes(_name).length > 0, "Name cannot be empty"); + + students[msg.sender] = Student({ + name: _name, + attendance: Attendance.Absent, + interests: new string[](0) + }); + + emit StudentCreated(msg.sender, _name); + } + + /// @notice Updates a student's attendance + function markAttendance(address _address, Attendance _attendance) + public + onlyOwner + studentExists(_address) + { + students[_address].attendance = _attendance; + emit AttendanceStatus(_address, _attendance); + } + + /// @notice Adds an interest to a student's profile + function addInterest(address _address, string memory _interest) + public + studentExists(_address) + { + require(bytes(_interest).length > 0, "Interest cannot be empty"); + require(students[_address].interests.length < 5, "Maximum 5 interests allowed"); + + // Check for duplicates + for(uint i = 0; i < students[_address].interests.length; i++) { + require( + keccak256(bytes(students[_address].interests[i])) != keccak256(bytes(_interest)), + "Interest already exists" + ); + } + + students[_address].interests.push(_interest); + emit InterestAdded(_address, _interest); + } + + /// @notice Removes an interest from a student's profile + function removeInterest(address _address, string memory _interest) + public + studentExists(_address) + { + string[] storage interests = students[_address].interests; + bool found = false; + uint indexToRemove; + + for(uint i = 0; i < interests.length; i++) { + if(keccak256(bytes(interests[i])) == keccak256(bytes(_interest))) { + indexToRemove = i; + found = true; + break; + } + } + + require(found, "Interest not found"); + + // Move the last element to the position of the element to remove + if(indexToRemove != interests.length - 1) { + interests[indexToRemove] = interests[interests.length - 1]; + } + interests.pop(); + + emit InterestRemoved(_address, _interest); + } + + /// @notice Gets a student's name + function getStudentName(address _address) + public + view + studentExists(_address) + returns (string memory) + { + return students[_address].name; + } + + /// @notice Gets a student's attendance + function getStudentAttendance(address _address) + public + view + studentExists(_address) + returns (Attendance) + { + return students[_address].attendance; + } + + /// @notice Gets a student's interests + function getStudentInterests(address _address) + public + view + studentExists(_address) + returns (string[] memory) + { + return students[_address].interests; + } + + /// @notice Transfers ownership of the contract + function transferOwnership(address _newOwner) public onlyOwner { + require(_newOwner != address(0), "New owner cannot be zero address"); + owner = _newOwner; + } +} \ No newline at end of file diff --git a/submissions/Assignment3/README.md b/submissions/Assignment3/README.md deleted file mode 100644 index 6f3a09c7..00000000 --- a/submissions/Assignment3/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Submissions for Assignment 3 ---- ---- \ No newline at end of file diff --git a/submissions/assignment-1.md b/submissions/assignment-1.md new file mode 100644 index 00000000..11dbdfbd --- /dev/null +++ b/submissions/assignment-1.md @@ -0,0 +1,12 @@ +# Assignment 1 Submission + +The implementation of the Counter Contract can be found at: +[Counter.sol](../../../assignments/fatiudeen/Counter.sol) + +This implementation includes: +- Complete counter functionality with increase/decrease operations +- Overflow and underflow protection +- Event emission for all counter changes +- Comprehensive error handling +- Clear NatSpec documentation +- Pure function for max uint256 calculation diff --git a/submissions/assignment-2.md b/submissions/assignment-2.md new file mode 100644 index 00000000..5470e632 --- /dev/null +++ b/submissions/assignment-2.md @@ -0,0 +1,12 @@ +# Assignment 2 Submission + +The implementation of the Student Registry can be found at: +[StudentRegistry.sol](../../../assignments/fatiudeen/StudentRegistry.sol) + +This implementation includes: +- Full student registration system +- Attendance tracking +- Interest management +- Comprehensive access controls +- Event emission for all major actions +- Security considerations and input validation