From 8da7d9c2f0fe468ce80721be0fb5772f814ce942 Mon Sep 17 00:00:00 2001 From: debbyprisca Date: Mon, 5 May 2025 18:59:09 +0300 Subject: [PATCH 1/3] | Register for OpenGuild x Encode Club Challenges --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee62858..f40fffd 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ git clone https://github.com/openguild-labs/open-encode-challenges.git Go to **Participant Registration** section and register to be the workshop participants. Add the below to the list, replace any placeholder with your personal information. ``` -| 🦄 | Name | Github username | Your current occupation | +| 🦄 |Deborah Prisca|debbyprisca|studentcd| ``` - Step 5: `Commit` your code and push to the forked Github repository From 56ecc7106963da84567cf9d261c30a39252e96e3 Mon Sep 17 00:00:00 2001 From: debbyprisca Date: Mon, 5 May 2025 20:19:44 +0300 Subject: [PATCH 2/3] | Register for OpenGuild x Encode Club Challenges --- README.md | 4 +- .../contracts/TokenVesting.sol | 85 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f40fffd..601211c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,9 @@ git clone https://github.com/openguild-labs/open-encode-challenges.git Go to **Participant Registration** section and register to be the workshop participants. Add the below to the list, replace any placeholder with your personal information. ``` -| 🦄 |Deborah Prisca|debbyprisca|studentcd| +| 🦄 |Deborah Prisca|debbyprisca|student + +| ``` - Step 5: `Commit` your code and push to the forked Github repository diff --git a/challenge-1-vesting/contracts/TokenVesting.sol b/challenge-1-vesting/contracts/TokenVesting.sol index 43d4c3a..026f6f3 100644 --- a/challenge-1-vesting/contracts/TokenVesting.sol +++ b/challenge-1-vesting/contracts/TokenVesting.sol @@ -31,18 +31,31 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard { struct VestingSchedule { // TODO: Define the vesting schedule struct + + uint256 totalAmount; // Total amount of tokens to be vested + uint256 startTime; // Start time of the vesting period + uint256 cliffDuration; // Duration of the cliff period in seconds + uint256 vestingDuration; // Duration of the vesting period in seconds + uint256 amountClaimed; // Amount of tokens already claimed + bool revoked; // Whether the vesting has been revoked } // Token being vested // TODO: Add state variables + IERC20 public token; + // Mapping from beneficiary to vesting schedule // TODO: Add state variables + mapping(address => VestingSchedule) public vestingSchedules; + // Whitelist of beneficiaries // TODO: Add state variables + mapping(address => bool) public whitelist; + // Events event VestingScheduleCreated(address indexed beneficiary, uint256 amount); event TokensClaimed(address indexed beneficiary, uint256 amount); @@ -53,6 +66,9 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard { constructor(address tokenAddress) { // TODO: Initialize the contract + require(tokenAddress != address(0), "Token address cannot be zero"); + token = IERC20(tokenAddress); + } // Modifier to check if beneficiary is whitelisted @@ -80,21 +96,90 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard { uint256 startTime ) external onlyOwner onlyWhitelisted(beneficiary) whenNotPaused { // TODO: Implement vesting schedule creation + + require(beneficiary != address(0), "Beneficiary cannot be zero address"); + require(amount > 0, "Amount must be greater than zero"); + require(vestingDuration > 0, "Vesting duration must be greater than zero"); + require(vestingDuration >= cliffDuration, "Vesting duration must be greater than or equal to cliff duration"); + require(vestingSchedules[beneficiary].totalAmount == 0, "Vesting schedule already exists"); } function calculateVestedAmount( address beneficiary ) public view returns (uint256) { // TODO: Implement vested amount calculation + + VestingSchedule storage schedule = vestingSchedules[beneficiary]; + + // If no schedule or revoked, return 0 + if (schedule.totalAmount == 0 || schedule.revoked) { + return 0; + } + // If current time is before cliff, nothing is vested + if (block.timestamp < schedule.startTime + schedule.cliffDuration) { + return 0; + } + + // If current time is after vesting duration, everything is vested + if (block.timestamp >= schedule.startTime + schedule.vestingDuration) { + return schedule.totalAmount; + } + + // Otherwise, calculate vested amount based on linear vesting + uint256 timeFromStart = block.timestamp - schedule.startTime; + uint256 vestedAmount = (schedule.totalAmount * timeFromStart) / schedule.vestingDuration; + + return vestedAmount; } function claimVestedTokens() external nonReentrant whenNotPaused { // TODO: Implement token claiming + + address beneficiary = msg.sender; + VestingSchedule storage schedule = vestingSchedules[beneficiary]; + + require(schedule.totalAmount > 0, "No vesting schedule found"); + require(!schedule.revoked, "Vesting schedule has been revoked"); + + uint256 vestedAmount = calculateVestedAmount(beneficiary); + uint256 claimableAmount = vestedAmount - schedule.amountClaimed; + + require(claimableAmount > 0, "No tokens available to claim"); + + // Update claimed amount + schedule.amountClaimed += claimableAmount; + + // Transfer tokens to beneficiary + require(token.transfer(beneficiary, claimableAmount), "Token transfer failed"); + + emit TokensClaimed(beneficiary, claimableAmount); } function revokeVesting(address beneficiary) external onlyOwner { // TODO: Implement vesting revocation + VestingSchedule storage schedule = vestingSchedules[beneficiary]; + + require(schedule.totalAmount > 0, "No vesting schedule found"); + require(!schedule.revoked, "Vesting schedule already revoked"); + + // Calculate vested amount + uint256 vestedAmount = calculateVestedAmount(beneficiary); + uint256 claimedAmount = schedule.amountClaimed; + + // Calculate unvested amount to return to owner + uint256 unvestedAmount = schedule.totalAmount - vestedAmount; + + // Mark schedule as revoked + schedule.revoked = true; + + // Transfer unvested tokens back to owner + if (unvestedAmount > 0) { + require(token.transfer(owner(), unvestedAmount), "Token transfer failed"); + } + + emit VestingRevoked(beneficiary); + } function pause() external onlyOwner { From ac109fbdd8505b71b3b0c6026e4e32543c0ca59a Mon Sep 17 00:00:00 2001 From: debbyprisca Date: Tue, 6 May 2025 16:50:53 +0300 Subject: [PATCH 3/3] | Register for OpenGuild x Encode Club Challenges --- .../contracts/TokenVesting.sol | 20 +++++++++---------- challenge-1-vesting/contracts/token.sol | 3 ++- challenge-2-yield-farm/contracts/yeild.sol | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/challenge-1-vesting/contracts/TokenVesting.sol b/challenge-1-vesting/contracts/TokenVesting.sol index 026f6f3..04d6b13 100644 --- a/challenge-1-vesting/contracts/TokenVesting.sol +++ b/challenge-1-vesting/contracts/TokenVesting.sol @@ -32,26 +32,26 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard { struct VestingSchedule { // TODO: Define the vesting schedule struct - uint256 totalAmount; // Total amount of tokens to be vested - uint256 startTime; // Start time of the vesting period - uint256 cliffDuration; // Duration of the cliff period in seconds - uint256 vestingDuration; // Duration of the vesting period in seconds - uint256 amountClaimed; // Amount of tokens already claimed - bool revoked; // Whether the vesting has been revoked + uint256 totalAmount; + uint256 startTime; + uint256 cliffDuration; + uint256 vestingDuration; + uint256 amountClaimed; + bool revoked; } - // Token being vested + // TODO: Add state variables IERC20 public token; - // Mapping from beneficiary to vesting schedule + // TODO: Add state variables mapping(address => VestingSchedule) public vestingSchedules; - // Whitelist of beneficiaries + // TODO: Add state variables mapping(address => bool) public whitelist; @@ -71,7 +71,7 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard { } - // Modifier to check if beneficiary is whitelisted + modifier onlyWhitelisted(address beneficiary) { require(whitelist[beneficiary], "Beneficiary not whitelisted"); _; diff --git a/challenge-1-vesting/contracts/token.sol b/challenge-1-vesting/contracts/token.sol index 5f952a1..edc4072 100644 --- a/challenge-1-vesting/contracts/token.sol +++ b/challenge-1-vesting/contracts/token.sol @@ -4,10 +4,11 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -contract MockERC20 is ERC20, Ownable(msg.sender) { +contract MockERC20 is ERC20, Ownable { constructor(string memory name, string memory symbol) ERC20(name, symbol) {} function mint(address to, uint256 amount) external onlyOwner { _mint(to, amount); } } + diff --git a/challenge-2-yield-farm/contracts/yeild.sol b/challenge-2-yield-farm/contracts/yeild.sol index 421496a..6ccf1fc 100644 --- a/challenge-2-yield-farm/contracts/yeild.sol +++ b/challenge-2-yield-farm/contracts/yeild.sol @@ -68,7 +68,7 @@ contract YieldFarm is ReentrancyGuard, Ownable { constructor( address _lpToken, address _rewardToken, - uint256 _rewardRate + uint256 _rewardRategit ) Ownable(msg.sender) { // TODO: Initialize contract state }