diff --git a/.vscode/settings.json b/.vscode/settings.json index bc1c7197..19035be5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "solidity.compileUsingRemoteVersion": "v0.8.14+commit.80d49f37" + "solidity.compileUsingRemoteVersion": "v0.8.24+commit.e11b9ed9" } \ No newline at end of file diff --git a/contracts/IStudentRegistry.sol b/contracts/IStudentRegistry.sol index b87e8bbe..1db6dae1 100644 --- a/contracts/IStudentRegistry.sol +++ b/contracts/IStudentRegistry.sol @@ -1,17 +1,61 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import "./Student.sol"; +import "contracts/StudentStruct.sol"; + +/** + * @title IStudentRegistry + * @dev Interface for the StudentRegistry contract. + * Defines the functions available for interacting with the student registry. + */ interface IStudentRegistry { - - function addStudent( - address _studentAddr, - string memory _name, - uint8 _age - ) external; - function getStudent(uint8 _studentID) external view returns (Student memory); + /** + * @dev pay Fees. + */ + function payFees() external payable; + + /** + @notice Withdraws the contract's balance to the owner. + @dev This function should handle the transfer of funds from the contract to the owner. + @return success A boolean indicating whether the withdrawal was successful. + */ + function withdrawEarnings() external returns (bool); + + /** + * @dev Register a student with the provided address, name, and age. + * @param _studentAddress The address of the student to register. + * @param _name The name of the student. + * @param _age The age of the student. + */ + function addStudent(address _studentAddress, string memory _name, uint8 _age) external payable; + + /** + * @dev Authorize a student for registration by setting their status to authorized. + * @param _studentAddress The address of the student to authorize. + */ + function authorizeStudent(address _studentAddress) external; + + /** + * @dev Retrieve a student information. + * @param _studentAddress The address of the student to retrieve. + */ + function getStudent(address _studentAddress) external view returns (Student memory); + + + /** + * @dev Update a student record. + * @param _studentAddress The new address of the student. + * @param _name The new name of the student. + * @param _age The new age of the student. + */ + function updateStudent(address _studentAddress, string memory _name, uint8 _age) external; + - function getStudentFromMapping(address _studentAddr) external view returns (Student memory); + /** + * @dev Delete a student record. + * @param _studentAddress The address of the student to delete. + */ + function deleteStudent(address _studentAddress) external; -} +} \ No newline at end of file diff --git a/contracts/MyStudentRegistry.sol b/contracts/MyStudentRegistry.sol deleted file mode 100644 index 10562f12..00000000 --- a/contracts/MyStudentRegistry.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.7.0 <0.9.0; -import "./IStudentRegistry.sol"; -import "./Student.sol"; -import "./Ownable.sol"; - -contract MyStudentRegistry is Ownable { - - address private StudentRegistryContractAddress; - - constructor(address _studentRgistry){ - StudentRegistryContractAddress = _studentRgistry; - } - - function registerStudent( - address _studentAddr, - string memory _name, - uint8 _age - ) public onlyOwner { - - IStudentRegistry(StudentRegistryContractAddress).addStudent(_studentAddr, _name, _age); - } - - - function getStudent2( - uint8 _studentId - ) public view returns (Student memory) { - - return IStudentRegistry(StudentRegistryContractAddress).getStudent(_studentId); - } -} diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol index 624bf251..6e61084b 100644 --- a/contracts/Ownable.sol +++ b/contracts/Ownable.sol @@ -1,33 +1,50 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.7.0 <0.9.0; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; contract Ownable { - address private owner; - - event ChangeOwner(address indexed oldOwner, address indexed newOwner); + address internal owner; + event replaceOwner(address indexed newOwner, address indexed oldOwner, string message); + + /** + * @dev Set the owner of the contract to the address deploying the contract. + */ constructor(){ - owner = msg.sender; + owner = payable (msg.sender); } - modifier onlyOwner { - require(owner == msg.sender, "Caller not owner"); + /** + * @dev restrict access to the contract owner only. + */ + modifier onlyOwner() { + require(owner == msg.sender, "Unauthorized"); _; } + + /** + * @dev Retrieve the current owner of the contract. + * @return The address of the current owner. + */ function getOwner() public view returns (address){ return owner; } - function changeOwner(address _newOwner) internal onlyOwner { + + /** + * @dev Change the owner of the contract to a new address. + * @param _newOwner The address of the new owner. + * Emits a {changedOwner} event. + */ + function changedOwner(address _newOwner) internal onlyOwner { require(_newOwner != address(0), "Owner can not be address zero"); - emit ChangeOwner(owner, _newOwner); + emit replaceOwner(_newOwner, owner, "This owner has been changed"); owner = _newOwner; } } \ No newline at end of file diff --git a/contracts/Payment.sol b/contracts/Payment.sol new file mode 100644 index 00000000..5c0ba48d --- /dev/null +++ b/contracts/Payment.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "./StudentStruct.sol"; +import "./modifiers/Ownable.sol"; +import "./modifiers/InputValidations.sol"; + +/** + * @title student Payment + * @dev This contract manages student registration payment, authorization, and details. + * It handles student registration payment. + */ +contract Payment is Ownable, InputValidation { + + event PaymentStatus(bool indexed hasPaid, string message); + + // Mapping of student address to payment receipt amount + mapping (address => uint256) internal PaymentDetails; + + + // Make payment + function payFees() public payable { + uint256 amount = msg.value; + require(amount == 1 ether, "1 eth is required"); + PaymentDetails[msg.sender] = amount; + emit PaymentStatus(true, "Payment successful!!"); + } + + /** + @notice Withdraws the contract's balance to the owner's address. + @return withdrawal indicating the withdrawal was successful. + */ + function withdrawEarnings() public onlyOwner returns(bool) { + // get the amount of Ether stored in this contract + uint256 amount = address(this).balance; + require(amount > 0, "Empty Balance"); + // send all Ether to owner + (bool withdrawal,) = owner.call{value: amount}(""); + require(withdrawal, "Failed to send Ether"); + return withdrawal; + } + + // Function to transfer Ether from this contract to address from input + function transfer(address payable _to, uint256 _amount) public onlyOwner { + (bool success,) = _to.call{value: _amount}(""); + require(success, "Failed to send Ether"); + } +} \ No newline at end of file diff --git a/contracts/Student.sol b/contracts/Student.sol deleted file mode 100644 index f7246ea0..00000000 --- a/contracts/Student.sol +++ /dev/null @@ -1,8 +0,0 @@ - // SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - struct Student { - address studentAddr; - string name; - uint256 studentId; - uint8 age; - } diff --git a/contracts/StudentRegistry.sol b/contracts/StudentRegistry.sol index 591618e1..a4a0c1dc 100644 --- a/contracts/StudentRegistry.sol +++ b/contracts/StudentRegistry.sol @@ -1,96 +1,86 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import "./Ownable.sol"; -import "./Student.sol"; - +import "contracts/modifiers/Ownable.sol"; +import "./IStudentRegistry.sol"; +/** + * @title MyStudentRegistry + * @dev This contract acts as a proxy to interact with another StudentRegistry contract. + * It forwards calls to the `StudentRegistry` contract for student management functions. + */ contract StudentRegistry is Ownable { - //custom erros - error NameIsEmpty(); - error UnderAge(uint8 age, uint8 expectedAge); - - //custom data type - - - //dynamic array of students - Student[] private students; - - mapping(address => Student) public studentsMapping; - - - modifier isNotAddressZero() { - require(msg.sender != address(0), "Invalid Address"); - _; + address public studentsContractAddress; + + /** + * @dev Set the address of the Students contract. + * @param _studentsContractAddr The address of the Student contract. + */ + constructor(address _studentsContractAddr) { + studentsContractAddress = _studentsContractAddr; } - function addStudent( - address _studentAddr, - string memory _name, - uint8 _age - ) public isNotAddressZero { - if (bytes(_name).length == 0) { - revert NameIsEmpty(); - } - - if (_age < 18) { - revert UnderAge({age: _age, expectedAge: 18}); - } - - uint256 _studentId = students.length + 1; - Student memory student = Student({ - studentAddr: _studentAddr, - name: _name, - age: _age, - studentId: _studentId - }); - - students.push(student); - // add student to studentsMapping - studentsMapping[_studentAddr] = student; + /** + * @dev Register a student. + * @param _studentAddress The address of the student to register. + * @param _name The name of the student. + * @param _age The age of the student. + */ + function addStudents(address _studentAddress, string memory _name, uint8 _age) public { + IStudentRegistry(studentsContractAddress).addStudent(_studentAddress, _name, _age); } - function getStudent(uint8 _studentId) - public - view - isNotAddressZero - returns (Student memory) - { - return students[_studentId - 1]; + /** + * @dev Retrieve a student record. + * @param _studentAddr The address of the student to retrieve. + */ + function getStudents(address _studentAddr) public view returns (Student memory) { + return IStudentRegistry(studentsContractAddress).getStudent(_studentAddr); } - function getStudentFromMapping(address _studentAddr) - public - view - isNotAddressZero - returns (Student memory) - { - return studentsMapping[_studentAddr]; + /** + * @dev Update a student record. + * @param _studentAddr The new address of the student. + * @param _name The new name of the student. + * @param _age The new age of the student. + */ + function updateStudents( + address _studentAddr, + string memory _name, + uint8 _age + ) public { + IStudentRegistry(studentsContractAddress).updateStudent(_studentAddr, _name, _age); } - function deleteStudent(address _studentAddr) - public - onlyOwner - isNotAddressZero - { - require( - studentsMapping[_studentAddr].studentAddr != address(0), - "Student does not exist" - ); - // delete studentsMapping[_studentAddr]; + /** + * @dev Delete a student record. + * @param _studentAddr The address of the student to delete. + * @notice only owner can delete a student + */ + function removeStudent(address _studentAddr) public { + IStudentRegistry(studentsContractAddress).deleteStudent(_studentAddr); + } - Student memory student = Student({ - studentAddr: address(0), - name: "", - age: 0, - studentId: 0 - }); - studentsMapping[_studentAddr] = student; + /** + @notice Pay Fees. + @dev This function calls the payFees function in the external contract.. + */ + function payFees() public payable { + return IStudentRegistry(studentsContractAddress).payFees{value: msg.value}(); } - - function modifyOwner(address _newOwner) public { - changeOwner(_newOwner); + /** + @notice Withdraws the contract's balance from the external Student Registry contract. + @dev This function calls the withdraw function in the external contract. + @return success A boolean value indicating whether the withdrawal was successful. + */ + function withdrawEarnings() public returns (bool) { + return IStudentRegistry(studentsContractAddress).withdrawEarnings(); } -} + + /** + * @dev Allow the contract to receive Ether. + */ + receive() external payable { } +} \ No newline at end of file diff --git a/contracts/StudentStruct.sol b/contracts/StudentStruct.sol new file mode 100644 index 00000000..a282bf60 --- /dev/null +++ b/contracts/StudentStruct.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/** + * @title Student + * @dev A Struct containing student information. + */ +struct Student { + /** + * @dev The address of the student. + */ + address studentAddress; + + /** + * @dev The unique ID assigned to the student. + */ + uint256 studentId; + + /** + * @dev The name of the student. + */ + string name; + + /** + * @dev The age of the student. + */ + uint8 age; + + /** + * @dev student authorization status. + */ + bool hasPaid; +} diff --git a/contracts/Students.sol b/contracts/Students.sol new file mode 100644 index 00000000..f8ae2286 --- /dev/null +++ b/contracts/Students.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "./StudentStruct.sol"; +import "./modifiers/Ownable.sol"; +import "./modifiers/InputValidations.sol"; +import "./Payment.sol"; + + + +/** + * @title Students + * @dev This contract manages student registrations, authorization, and details. + * It allows for student registration with payment, updating student information, and handling ownership. + */ +contract Students is Payment, InputValidation, Ownable { + event createEvent(address indexed studentAddress, string message); + event updateEvent(address indexed studentAddress, string message); + // Mapping of student address to their details + mapping (address => Student) private students; + uint256 private studentCount = 0; + + + /** + * @dev Register a student with a given address, name, and age. + * Requires payment of exactly 1 ether. + * @param _studentAddress The address of the student. + * @param _name The name of the student. + * @param _age The age of the student. + */ + + function addStudent(address _studentAddress, string memory _name, uint8 _age) public validateAddress(_studentAddress) validateStudentAge(_age) validateStudentName(_name) onlyOwner { + + uint256 hasPaid = PaymentDetails[_studentAddress]; + + require(hasPaid > 0 ether, "payment required"); + + uint256 _studentId = studentCount + 1; + Student memory student = Student({ + studentAddress: _studentAddress, + name: _name, + age: _age, + studentId: _studentId, + hasPaid:true + }); + + // add student to students Mapping + students[_studentAddress] = student; + emit createEvent(_studentAddress, "student added"); + } + + /** + * @dev Retrieve the student by address. + * @return The student record. + */ + function getStudent(address _studentaddr) + public + view + validateAddress(_studentaddr) + returns (Student memory) + { + require(students[_studentaddr].studentAddress != address(0), "student does not exist"); + return students[_studentaddr]; + } + + + + /** + * @dev Delete a student record. Only the owner can delete a student. + * @param _studentAddr The address of the student to delete. + */ + function deleteStudent(address _studentAddr) + public + onlyOwner + validateAddress(_studentAddr) + { + require( + students[_studentAddr].studentAddress != address(0), + "Student does not exist" + ); + + delete students[_studentAddr]; + } + + + + /** + * @dev Update student record. + * Can change name, or age of the student. Only the owner can update. + * @param _studentAddr The new address of the student. + * @param _name The new name of the student. + * @param _age The new age of the student. + */ + function updateStudent( + address _studentAddr, + string memory _name, + uint8 _age + ) public validateAddress(_studentAddr) { +require( + students[_studentAddr].studentAddress != address(0), + "Student does not exist" + ); + + Student memory student = students[_studentAddr]; + student.name = _name; + student.age = _age; + students[_studentAddr] = student; + emit updateEvent(_studentAddr, "update successful"); + } + + /** + * @dev Allow the contract to receive Ether. + */ + receive() external payable { } +} \ No newline at end of file diff --git a/contracts/modifiers/InputValidations.sol b/contracts/modifiers/InputValidations.sol new file mode 100644 index 00000000..2b038679 --- /dev/null +++ b/contracts/modifiers/InputValidations.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/** +* @title Input Validation Contract +* @notice This contract contains modifiers to validate inputs for student registration +*/ +contract InputValidation { + /** + *@dev Ensures that the student is at least 18 years old. + * @notice Modifier to validate the age of the student + *@param _age The age of the student + */ + modifier validateStudentAge(uint256 _age) { + require( _age >= 18, "This student is under age"); + _; + } + + + /** + *@dev Ensures that the name is not blank. + * @notice Modifier to validate the name of the student + * @param _name The name of the student + */ + modifier validateStudentName(string memory _name) { + require( bytes(_name).length > 0, "Name cannot be blank"); + _; + } + + /** + * @dev Ensures that the student ID is greater than zero. + * @notice Modifier to validate the ID of the student + * @param _studentId The ID of the student + */ + modifier validateStudentId(uint8 _studentId) { + require(_studentId > 0, "Invalid Student ID"); + _; + } + + + /** + * @dev Ensures that the address is not the zero address. + * @notice Modifier to validate the address of the student + * @param _studentAddr The address of the student + */ + modifier validateAddress(address _studentAddr) { + require( _studentAddr != address(0), "Invalid Address"); + _; + } +} \ No newline at end of file diff --git a/contracts/modifiers/Ownable.sol b/contracts/modifiers/Ownable.sol new file mode 100644 index 00000000..6e61084b --- /dev/null +++ b/contracts/modifiers/Ownable.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + + +contract Ownable { + + address internal owner; + event replaceOwner(address indexed newOwner, address indexed oldOwner, string message); + + + /** + * @dev Set the owner of the contract to the address deploying the contract. + */ + constructor(){ + owner = payable (msg.sender); + } + + + /** + * @dev restrict access to the contract owner only. + */ + modifier onlyOwner() { + require(owner == msg.sender, "Unauthorized"); + _; + } + + + /** + * @dev Retrieve the current owner of the contract. + * @return The address of the current owner. + */ + + function getOwner() public view returns (address){ + return owner; + } + + + + /** + * @dev Change the owner of the contract to a new address. + * @param _newOwner The address of the new owner. + * Emits a {changedOwner} event. + */ + function changedOwner(address _newOwner) internal onlyOwner { + require(_newOwner != address(0), "Owner can not be address zero"); + + emit replaceOwner(_newOwner, owner, "This owner has been changed"); + owner = _newOwner; + } +} \ No newline at end of file