Skip to content
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
| 🦄 | Ruth Chisom | ruthchisom | Blockchain & Backend Developer |
```

- Step 5: `Commit` your code and push to the forked Github repository
Expand Down
2 changes: 1 addition & 1 deletion challenge-1-vesting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Add your information to the below list to officially participate in the workshop

| Emoji | Name | Github Username | Occupations |
| ----- | ---- | ------------------------------------- | ----------- |
| 🎅 | Ippo | [NTP-996](https://github.com/NTP-996) | DevRel |
| 🎅 | Ruth Chisom | [ruthchisom](https://github.com/ruthchisom) | Blockchain & Backend Developer |

## 💻 Local development environment setup

Expand Down
74 changes: 70 additions & 4 deletions challenge-1-vesting/contracts/TokenVesting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,24 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
struct VestingSchedule {
// TODO: Define the vesting schedule struct
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;
// Events
event VestingScheduleCreated(address indexed beneficiary, uint256 amount);
event TokensClaimed(address indexed beneficiary, uint256 amount);
Expand All @@ -52,7 +58,8 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {

constructor(address tokenAddress) {
// TODO: Initialize the contract

require(tokenAddress != address(0), "Invalid token address");
token = IERC20(tokenAddress);
}

// Modifier to check if beneficiary is whitelisted
Expand Down Expand Up @@ -80,20 +87,79 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
uint256 startTime
) external onlyOwner onlyWhitelisted(beneficiary) whenNotPaused {
// TODO: Implement vesting schedule creation
require(beneficiary != address(0), "Invalid beneficiary");
require(amount > 0, "Amount must be greater than zero");
require(vestingDuration >= cliffDuration, "Vesting must be >= cliff");
require(vestingSchedules[beneficiary].totalAmount == 0, "Schedule exists");

vestingSchedules[beneficiary] = VestingSchedule({
totalAmount: amount,
startTime: startTime,
cliffDuration: cliffDuration,
vestingDuration: vestingDuration,
amountClaimed: 0,
revoked: false
});

require(token.transferFrom(msg.sender, address(this), amount), "Token transfer failed");

emit VestingScheduleCreated(beneficiary, amount);
}

function calculateVestedAmount(
address beneficiary
) public view returns (uint256) {
// TODO: Implement vested amount calculation
VestingSchedule memory schedule = vestingSchedules[beneficiary];

if (schedule.revoked) {
return 0;
}

uint256 currentTime = block.timestamp;

if (currentTime < schedule.startTime + schedule.cliffDuration) {
return 0;
}

if (currentTime >= schedule.startTime + schedule.vestingDuration) {
return schedule.totalAmount;
}

uint256 timeElapsed = currentTime - schedule.startTime;
return (schedule.totalAmount * timeElapsed) / schedule.vestingDuration;
}

function claimVestedTokens() external nonReentrant whenNotPaused {
// TODO: Implement token claiming
VestingSchedule storage schedule = vestingSchedules[msg.sender];
require(schedule.totalAmount > 0, "No schedule");

uint256 vested = calculateVestedAmount(msg.sender);
uint256 claimable = vested - schedule.amountClaimed;
require(claimable > 0, "No tokens to claim");

schedule.amountClaimed += claimable;
require(token.transfer(msg.sender, claimable), "Token transfer failed");

emit TokensClaimed(msg.sender, claimable);
}

function revokeVesting(address beneficiary) external onlyOwner {
// TODO: Implement vesting revocation
VestingSchedule storage schedule = vestingSchedules[beneficiary];
require(schedule.totalAmount > 0, "No schedule");
require(!schedule.revoked, "Already revoked");

uint256 vested = calculateVestedAmount(beneficiary);
uint256 unvested = schedule.totalAmount - vested;
schedule.revoked = true;

if (unvested > 0) {
require(token.transfer(owner(), unvested), "Return unvested failed");
}

emit VestingRevoked(beneficiary);

}

Expand Down
Loading