diff --git a/contracts/Assignment-1/counter.enyinnaya1234.sol b/contracts/Assignment-1/counter.enyinnaya1234.sol new file mode 100644 index 00000000..b734e2ea --- /dev/null +++ b/contracts/Assignment-1/counter.enyinnaya1234.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; +/** +* @title Counter +* @author Enyinnaya Wisdom +* @notice a counter contract that allows for incrementing and decrementing a counter +* @dev This contract demonstrates basic state management and event emission +*/ +contract Counter { + uint public counter; + /** + * @notice Emitted when the counter is increased. + * @param newValue The new value of the counter after incrementing. + * @param timestamp The block timestamp when the counter was increased. + */ + event CountIncreased(uint256 newValue, uint256 timestamp); + + /** + * @notice Emitted when the counter is decreased. + * @param newValue The new value of the counter after decrementing. + * @param timestamp The block timestamp when the counter was decreased. + */ + event CountDecreased(uint256 newValue, uint256 timestamp); + /** + * @notice Increases the counter by 1. + * @dev This function increments the counter and emits a `CountIncreased` event. + * It automatically checks for overflow in Solidity 0.8.0+. + */ + function increaseByOne() public { + require(counter < type(uint256).max, "cannot increase beyond max uint"); + counter ++; + emit CountIncreased(counter, block.timestamp); + } + /** + * @notice Increases the counter by a specified value. + * @dev This function increments the counter by `_value` and emits a `CountIncreased` event. + * It reverts if the counter would exceed the maximum value for `uint256`. + * @param _value The amount by which to increase the counter. + */ + function increaseByValue(uint _value) public { + require(counter < type(uint256).max, "cannot increase beyond max uint"); + counter += _value; + emit CountIncreased(counter, block.timestamp); + + } + /** + * @notice Decreases the counter by 1. + * @dev This function decrements the counter and emits a `CountDecreased` event. + * It reverts if the counter is already 0 to prevent underflow. + */ + function decreaseByOne() public { + require(counter > 1, "cannot decrease below 0"); + counter --; + emit CountDecreased(counter, block.timestamp); + } + + /** + * @notice Decreases the counter by a specified value. + * @dev This function decrements the counter by `_value` and emits a `CountDecreased` event. + * It reverts if the counter would go below 0. + * @param _value The amount by which to decrease the counter. + */ + function decreaseByValue(uint _value) public { + require(counter > _value, "cannot decrease below 0"); + counter -= _value; + emit CountDecreased(counter, block.timestamp); + } + /** + * @notice Resets the counter to 0. + * @dev This function sets the counter to 0 and emits a `CountDecreased` event. + */ + function resetCount() public { + counter = 0; + emit CountDecreased(counter, block.timestamp); + } + /** + * @notice Returns the current value of the counter. + * @dev This is a getter function for the `counter` state variable. + * @return The current value of the counter. + */ + function getCount() public view returns (uint) { + return counter; + } + /** + * @notice Sets the counter to a specific value. + * @dev This function updates the counter to `_num` and emits a `CountIncreased` event. + * @param _num The new value to set the counter to. + */ + function stateChanging(uint _num) public { + counter = _num; + emit CountIncreased(counter, block.timestamp); + } + +} \ No newline at end of file diff --git a/contracts/Assignment-2/studentRegistry.enyinnaya1234.sol b/contracts/Assignment-2/studentRegistry.enyinnaya1234.sol new file mode 100644 index 00000000..a63ade2c --- /dev/null +++ b/contracts/Assignment-2/studentRegistry.enyinnaya1234.sol @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +/** + * @title StudentRegistry + * @dev Manages student records, attendance, and interests. + */ +contract StudentRegistry { + + /// @notice Enum representing student attendance status. + enum Attendance { + Present, + Absent + } + + /// @notice Struct to store student details. + struct studentDetails { + string name; + Attendance attendance; + string[] interest; + } + + /// @notice Address of the contract owner. + address public owner; + + /// @notice Event emitted when ownership is transferred. + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /// @notice Sets the deployer as the initial owner. + constructor() { + owner = msg.sender; + emit OwnershipTransferred(address(0), owner); + } + + /// @notice Event emitted when a new student is registered. + event createdStudent(string name, Attendance attendance, string[] interest); + + /// @notice Event emitted when a student's attendance status is updated. + event AttendanceStatus(address studentAddress, Attendance attendanceStatus); + + /// @notice Event emitted when an interest is added. + event InterestAdded(address studentAddress, string interest); + + /// @notice Event emitted when an interest is removed. + event InterestRemoved(address studentAddress, string interest); + + /// @notice Mapping of student addresses to their details. + mapping(address => studentDetails) public student; + + /// @notice Ensures only the contract owner can execute a function. + modifier onlyOwner() { + require(msg.sender == owner, "Only the contract owner can perform this action."); + _; + } + + /// @notice Ensures a student exists before executing a function. + modifier studentExists(address _studentaddr) { + require(bytes(student[_studentaddr].name).length != 0, "Student does not exist."); + _; + } + + /// @notice Ensures a student does not already exist before executing a function. + modifier studentDoesNotExist(address _studentAddr) { + require(bytes(student[_studentAddr].name).length == 0, "Student already exists."); + _; + } + + /// @notice Registers a new student with a default name and interests. + function registerStudent() public studentDoesNotExist(msg.sender) { + student[msg.sender].name = "Goodness"; + student[msg.sender].attendance = Attendance.Absent; + student[msg.sender].interest.push("football"); + student[msg.sender].interest.push("eating"); + student[msg.sender].interest.push("Youtube"); + student[msg.sender].interest.push("coding"); + student[msg.sender].interest.push("gaming"); + emit createdStudent(student[msg.sender].name, student[msg.sender].attendance, student[msg.sender].interest); + } + + /// @notice Registers a student with a specified name. + function registerNewStudent(string memory _name) public studentDoesNotExist(msg.sender) { + student[msg.sender].name = _name; + student[msg.sender].attendance = Attendance.Absent; + emit createdStudent(_name, Attendance.Absent, new string[](0)); + } + + /// @notice Updates a student's attendance status. + function markAttendance(address _address, Attendance _attendance) public studentExists(_address) { + student[_address].attendance = _attendance; + emit AttendanceStatus(_address, _attendance); + } + + /// @notice Adds an interest to a student's record. + function addInterest(address _address, string memory _interest) public studentExists(_address) { + require(bytes(_interest).length > 0, "Interest cannot be empty."); + require(student[_address].interest.length < 5, "Cannot have more than 5 interests."); + for (uint i = 0; i < student[_address].interest.length; i++) { + require( + keccak256(bytes(student[_address].interest[i])) != keccak256(bytes(_interest)), + "Interest already exists." + ); + } + student[_address].interest.push(_interest); + emit InterestAdded(_address, _interest); + } + + /// @notice Removes an interest from a student's record. + function removeInterest(address _address, string memory _interest) public studentExists(_address) { + bool interestFound = false; + uint indexToRemove; + for (uint i = 0; i < student[_address].interest.length; i++) { + if (keccak256(bytes(student[_address].interest[i])) == keccak256(bytes(_interest))) { + interestFound = true; + indexToRemove = i; + break; + } + } + require(interestFound, "Interest not found."); + uint lastIndex = student[_address].interest.length - 1; + if (indexToRemove != lastIndex) { + student[_address].interest[indexToRemove] = student[_address].interest[lastIndex]; + } + student[_address].interest.pop(); + emit InterestRemoved(_address, _interest); + } + + /// @notice Retrieves a student's name. + function getStudentName(address _address) public view studentExists(_address) returns (string memory) { + return student[_address].name; + } + + /// @notice Retrieves a student's attendance status. + function getStudentAttendance(address _address) public view studentExists(_address) returns (Attendance) { + return student[_address].attendance; + } + + /// @notice Retrieves a student's interests. + function getStudentInterests(address _address) public view studentExists(_address) returns (string[] memory) { + return student[_address].interest; + } + + /// @notice Transfers contract ownership to a new address. + function transferOwnership(address _newOwner) public onlyOwner { + require(_newOwner != address(0), "New owner cannot be the zero address."); + address previousOwner = owner; + owner = _newOwner; + emit OwnershipTransferred(previousOwner, _newOwner); + } +} \ No newline at end of file diff --git a/contracts/Assignment-3/CrowdFundingContract.sol b/contracts/Assignment-3/CrowdFundingContract.sol new file mode 100644 index 00000000..babcdadb --- /dev/null +++ b/contracts/Assignment-3/CrowdFundingContract.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + +import "./ERC20.sol"; +import "./ERC721.sol"; + +contract CrowdFundingContract{ + bool public fundingGoalMet = false; + uint public fundingThreshold; + uint public fundingGoal; + address public owner; + + // initialize imported files + ERC20 private erc20; + ERC721 private erc721; + + // Mapping + mapping(address => uint256) public _tokenBalances; + mapping(address => uint256) public _contributions; + + // Events + event trackContributions(address contributor, uint amount); + + constructor(uint _fundingGoal, uint _fundingThreshold, address payable Erc20Address, address Erc721Address){ + fundingGoal = _fundingGoal; + fundingThreshold = _fundingThreshold; + owner = msg.sender; + erc20 = ERC20(Erc20Address); //0xdA36e707Af22009FA453Fe3D2707c510C8E4AC46 + erc721 = ERC721(Erc721Address); //0xee41a93503C6007c72BbfEc027412430B3c743F0 + } + + + // modifiers + modifier onlyOwner(){ + require(msg.sender == owner, "Not owner"); + _; + } + + // Functions + // Tracking contributions + // Contribution exceed a set threshold, mint an ERC721 NFT and send to contributor's account + function exceedThreshold(address to ) external onlyOwner payable { + if (address(this).balance < fundingGoal){ + require(msg.value > 0, "contributions must be above 0"); + _contributions[msg.sender] += msg.value; + if(msg.value > fundingThreshold){ + uint256 tokenId = uint256(keccak256(abi.encodePacked(msg.sender))); + erc721._mint(to, tokenId); + } + else{ + erc20.mint(to, msg.value); + } + } + else{ + // withdraw funds + uint balance = address(this).balance; + address payable sendTo = payable(owner); + sendTo.transfer(balance); + fundingGoalMet = true; + } + + } + + +} \ No newline at end of file diff --git a/contracts/Assignment-3/ERC20.sol b/contracts/Assignment-3/ERC20.sol new file mode 100644 index 00000000..e3bc864f --- /dev/null +++ b/contracts/Assignment-3/ERC20.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + + +contract ERC20 { + error FunctionDoesNotExist(); + string public name; + string public symbol; + uint8 public decimals ; + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + //mapping(address) + + event Transfer(address indexed _from, address indexed _to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + constructor(uint _totalSupply, string memory _name, string memory _symbol, uint8 _decimals){ + _totalSupply = totalSupply; + _name = name; + _symbol = symbol; + _decimals = decimals; + } + + receive() external payable{} + fallback() external payable{ + revert FunctionDoesNotExist(); + } + + function transfer(address recipient, uint256 amount) external returns (bool){ + require(balanceOf[msg.sender] >= amount, "Insufficient funds"); + balanceOf[msg.sender] -= amount; + balanceOf[recipient] += amount; + + emit Transfer(msg.sender, recipient, amount); + return true; + } + + function mint(address _to, uint256 _amount) external{ + require(msg.sender != address(0), "zero address"); + balanceOf[_to] += _amount; + totalSupply += _amount; + + emit Transfer(address(0), _to, _amount); + } +} \ No newline at end of file diff --git a/contracts/Assignment-3/ERC721.sol b/contracts/Assignment-3/ERC721.sol new file mode 100644 index 00000000..58f29ff3 --- /dev/null +++ b/contracts/Assignment-3/ERC721.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + + +contract ERC721 { + // Mapping + mapping(uint256 => address) internal _ownerOf; + mapping(address => uint256) internal _balanceOf; + + // event + event Transfer(address indexed from, address indexed to, uint256 indexed id); + + function _mint(address to, uint256 tokenId) external{ + require(to != address(0), "mint to zero address"); + require(_ownerOf[tokenId] == address(0), "already minted"); + + _balanceOf[to]++; + _ownerOf[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + } + + function balanceOf(address owner) external view returns (uint256){ + require(owner != address(0), "owner = zero address"); + return _balanceOf[owner]; + } + + function ownerOf(uint256 tokenId) external view returns (address ownerOfToken){ + ownerOfToken = _ownerOf[tokenId]; + require(ownerOfToken != address(0), "token doesn't exist"); +} +} \ No newline at end of file diff --git a/submissions/Assignment-3.md b/submissions/Assignment-3.md new file mode 100644 index 00000000..4a303953 --- /dev/null +++ b/submissions/Assignment-3.md @@ -0,0 +1,2 @@ +# Assignment-3 +Here's a link to [assignment-3]{../contracts/Assignment-3/CrowdFundingContract.sol} \ No newline at end of file diff --git a/submissions/Assignment1.md b/submissions/Assignment1.md new file mode 100644 index 00000000..b746091b --- /dev/null +++ b/submissions/Assignment1.md @@ -0,0 +1,2 @@ +# Assignment-1 +Here is a link to [Assignment-1]{./contracts/Assignment-2/studentRegistry.enyinnaya1234.sol} \ No newline at end of file diff --git a/submissions/Assignment2.md b/submissions/Assignment2.md new file mode 100644 index 00000000..3d34b1af --- /dev/null +++ b/submissions/Assignment2.md @@ -0,0 +1,2 @@ +# Assignment-3 +Here is a link to [Assignment-3]{../contracts/Assignment-3/CrowdFundingContract.sol} \ No newline at end of file diff --git a/submissions/Assignment3/enyinnaya1234/CrowdFundingContract.sol b/submissions/Assignment3/enyinnaya1234/CrowdFundingContract.sol new file mode 100644 index 00000000..dd9d8d12 --- /dev/null +++ b/submissions/Assignment3/enyinnaya1234/CrowdFundingContract.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + +import "./ERC20.sol"; +import "./ERC721.sol"; + +contract CrowdFundingContract{ + bool public fundingGoalMet; + uint public fundingThreshold; + uint public fundingGoal; + address public owner; + + // Mapping + mapping(address => uint256) public _tokenBalances; + mapping(address => uint256) public _contributions; + + // Events + Events trackContributions(address contributor, uint amount); + + constructor(uint memory _fundingGoal, uint memory _fundingThreshold){ + _fundingGoal = fundingGoal; + _fundingThreshold = fundingThreshold; + owner = msg.sender; + } + + // modifiers + modifier onlyOwner(){ + require(msg.sender == owner, "Not owner"); + + _; + } + + // Functions + + // Tracking contributions + // Contribution exceed a set threshold, mint an ERC721 NFT + and send to contributor's account + function exceedThreshold(address from, uint tokenId, address to ) external { + _contributions[from] = msg.value; + if(_contributions[from][msg.value] > _fundingThreshold){ + ERC721._mint(to, tokenId); + } + else{ + ERC20._mint(contributor, msg.value); + } + } + + // Project owner can withdraw funds if a funding goal is met + function goalIsMet() onlyOwner { + if (fundingGoalMet){ + // withdraw funds + + } +} \ No newline at end of file diff --git a/submissions/Assignment3/enyinnaya1234/ERC20.sol b/submissions/Assignment3/enyinnaya1234/ERC20.sol new file mode 100644 index 00000000..28b0eefc --- /dev/null +++ b/submissions/Assignment3/enyinnaya1234/ERC20.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + +import "./IERC20.sol"; + +contract ERC20 is IERC20{ + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + + mapping(address => uint256) public balance; + //mapping(address) + + event Transfer(address indexed _from, address indexed _to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + constructor(string memory _name, string memory _symbol, uint8 _decimals){ + name = _name; + symbol = _symbol; + decimals = _decimals; + } + + function transfer(address recipient, uint256 amount) external returns (bool){ + require(balanceOf[msg.sender] >= amount, "Insufficient funds"); + balanceOf[msg.sender] -= amount; + balanceOf[recipient] += amount; + + emit Transfer(msg.sender, recipient, amount); + } + + function mint(address _to, uint256 _amount) external{ + require(msg.sender != address(0), "zero address"); + balanceOf[_to] += _amount; + totalSupply += _amount; + + emit Transfer(address(0), _to, _amount); + } +} \ No newline at end of file diff --git a/submissions/Assignment3/enyinnaya1234/ERC721.sol b/submissions/Assignment3/enyinnaya1234/ERC721.sol new file mode 100644 index 00000000..58f29ff3 --- /dev/null +++ b/submissions/Assignment3/enyinnaya1234/ERC721.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + + +contract ERC721 { + // Mapping + mapping(uint256 => address) internal _ownerOf; + mapping(address => uint256) internal _balanceOf; + + // event + event Transfer(address indexed from, address indexed to, uint256 indexed id); + + function _mint(address to, uint256 tokenId) external{ + require(to != address(0), "mint to zero address"); + require(_ownerOf[tokenId] == address(0), "already minted"); + + _balanceOf[to]++; + _ownerOf[tokenId] = to; + + emit Transfer(address(0), to, tokenId); + } + + function balanceOf(address owner) external view returns (uint256){ + require(owner != address(0), "owner = zero address"); + return _balanceOf[owner]; + } + + function ownerOf(uint256 tokenId) external view returns (address ownerOfToken){ + ownerOfToken = _ownerOf[tokenId]; + require(ownerOfToken != address(0), "token doesn't exist"); +} +} \ No newline at end of file diff --git a/submissions/Assignment3/enyinnaya1234/IERC20.sol b/submissions/Assignment3/enyinnaya1234/IERC20.sol new file mode 100644 index 00000000..e3eaf609 --- /dev/null +++ b/submissions/Assignment3/enyinnaya1234/IERC20.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + +interface IERC20{ + function transfer(address recipient, uint256 amount) external returns (bool); + function mint(address _to, uint256 _amount) external; +} \ No newline at end of file diff --git a/submissions/Assignment3/enyinnaya1234/IERC721.sol b/submissions/Assignment3/enyinnaya1234/IERC721.sol new file mode 100644 index 00000000..9a3db19c --- /dev/null +++ b/submissions/Assignment3/enyinnaya1234/IERC721.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.24; + +interface IERC721{ + function _mint(address to, uint256 tokenId) external; +} \ No newline at end of file