Skip to content

Task 2 #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: task-2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions contracts/StudentRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract StudentRegistry {
// Attendance status.
enum Attendance { Present, Absent }

// Student data structure.
struct Student {
string name;
Attendance attendance;
string[] interests;
}

mapping(address => Student) public students;
address public owner;

event StudentCreated(address studentAddress, string name);
event AttendanceStatus(address studentAddress, Attendance attendance);
event InterestAdded(address studentAddress, string interest);
event InterestRemoved(address studentAddress, string interest);

modifier onlyOwner() {
require(msg.sender == owner, "Only owner");
_;
}

modifier studentExists(address studentAddress) {
require(bytes(students[studentAddress].name).length != 0, "Student does not exist");
_;
}

modifier studentDoesNotExist(address studentAddress) {
require(bytes(students[studentAddress].name).length == 0, "Student already exists");
_;
}

constructor() {
owner = msg.sender;
}

// Register a student with full details.
function registerStudent(
string memory name,
Attendance attendance,
string[] memory interests
) public studentDoesNotExist(msg.sender) {
require(bytes(name).length > 0, "Name cannot be empty");
students[msg.sender] = Student(name, attendance, interests);
emit StudentCreated(msg.sender, name);
}

// Register a student with a name and default attendance (Absent).
function registerNewStudent(string memory name)
public
studentDoesNotExist(msg.sender)
{
require(bytes(name).length > 0, "Name cannot be empty");
students[msg.sender] = Student(name, Attendance.Absent, new string[](0));
emit StudentCreated(msg.sender, name);
}

// Update a student's attendance.
function markAttendance(address studentAddress, Attendance attendance)
public
studentExists(studentAddress)
{
students[studentAddress].attendance = attendance;
emit AttendanceStatus(studentAddress, attendance);
}

// Add an interest to a student's profile.
function addInterest(address studentAddress, string memory interest)
public
studentExists(studentAddress)
{
require(bytes(interest).length > 0, "Interest cannot be empty");
Student storage student = students[studentAddress];
require(student.interests.length < 5, "Max interests reached");

for (uint i = 0; i < student.interests.length; i++) {
require(keccak256(bytes(student.interests[i])) != keccak256(bytes(interest)), "Duplicate interest");
}
student.interests.push(interest);
emit InterestAdded(studentAddress, interest);
}

// Remove an interest from a student's profile.
function removeInterest(address studentAddress, string memory interest)
public
studentExists(studentAddress)
{
Student storage student = students[studentAddress];
uint length = student.interests.length;
bool found = false;
for (uint i = 0; i < length; i++) {
if (keccak256(bytes(student.interests[i])) == keccak256(bytes(interest))) {
student.interests[i] = student.interests[length - 1];
student.interests.pop();
found = true;
emit InterestRemoved(studentAddress, interest);
break;
}
}
require(found, "Interest not found");
}

// Get the student's name.
function getStudentName(address studentAddress)
public
view
studentExists(studentAddress)
returns (string memory)
{
return students[studentAddress].name;
}

// Get the student's attendance.
function getStudentAttendance(address studentAddress)
public
view
studentExists(studentAddress)
returns (Attendance)
{
return students[studentAddress].attendance;
}

// Get the student's interests.
function getStudentInterests(address studentAddress)
public
view
studentExists(studentAddress)
returns (string[] memory)
{
return students[studentAddress].interests;
}

// Transfer contract ownership.
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0), "Invalid address");
owner = newOwner;
}

// (Bonus) Update the caller's name.
function updateStudentName(string memory newName)
public
studentExists(msg.sender)
{
require(bytes(newName).length > 0, "New name empty");
students[msg.sender].name = newName;
emit StudentCreated(msg.sender, newName);
}
}
2 changes: 2 additions & 0 deletions submissions/assignment-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Assignment 2
Here's a link to [assignment 2](../contracts/studentRegistry.sol)
69 changes: 69 additions & 0 deletions test/StudentRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("StudentRegistry", function () {
let StudentRegistry, studentRegistry, owner, addr1, addr2;

beforeEach(async function () {
[owner, addr1, addr2] = await ethers.getSigners();
StudentRegistry = await ethers.getContractFactory("StudentRegistry");
studentRegistry = await StudentRegistry.deploy();
});

it("should deploy with the correct owner", async function () {
expect(await studentRegistry.owner()).to.equal(owner.address);
});

it("should allow a student to register", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
const studentName = await studentRegistry.getStudentName(addr1.address);
expect(studentName).to.equal("Alice");
});

it("should prevent duplicate registration", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
await expect(
studentRegistry.connect(addr1).registerNewStudent("Alice")
).to.be.revertedWith("Student already exists");
});

it("should allow marking attendance", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
await studentRegistry.markAttendance(addr1.address, 1); // 1 = Present
expect(await studentRegistry.getStudentAttendance(addr1.address)).to.equal(1);
});

it("should allow adding interests", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
await studentRegistry.addInterest(addr1.address, "Blockchain");
const interests = await studentRegistry.getStudentInterests(addr1.address);
expect(interests).to.include("Blockchain");
});

it("should prevent duplicate interests", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
await studentRegistry.addInterest(addr1.address, "Blockchain");
await expect(
studentRegistry.addInterest(addr1.address, "Blockchain")
).to.be.revertedWith("Duplicate interest");
});

it("should allow removing interests", async function () {
await studentRegistry.connect(addr1).registerNewStudent("Alice");
await studentRegistry.addInterest(addr1.address, "Blockchain");
await studentRegistry.removeInterest(addr1.address, "Blockchain");
const interests = await studentRegistry.getStudentInterests(addr1.address);
expect(interests).to.not.include("Blockchain");
});

it("should allow owner to transfer ownership", async function () {
await studentRegistry.transferOwnership(addr1.address);
expect(await studentRegistry.owner()).to.equal(addr1.address);
});

it("should prevent non-owners from transferring ownership", async function () {
await expect(
studentRegistry.connect(addr1).transferOwnership(addr2.address)
).to.be.revertedWith("Only owner");
});
});