Skip to content

Okhayeeli | Register for OpenGuild x Encode Club Solidity #10

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 5 commits into
base: main
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
7 changes: 4 additions & 3 deletions challenge-1-vesting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ OpenGuild Labs makes the repository to introduce OpenHack workshop participants

Add your information to the below list to officially participate in the workshop challenge (This is the first mission of the whole workshop)

| Emoji | Name | Github Username | Occupations |
| ----- | ---- | ------------------------------------- | ----------- |
| 🎅 | Ippo | [NTP-996](https://github.com/NTP-996) | DevRel |
| Emoji | Name | Github Username | Occupations |
| ----- | ---- | ------------------------------------- | ----------- |
| 🎅 | Ippo | [NTP-996](https://github.com/NTP-996) | DevRel |
| 👧 | Elizabeth | [Okhayeeli](https://github.com/Okhayeeli/) | software engineer |

## 💻 Local development environment setup

Expand Down
94 changes: 85 additions & 9 deletions challenge-1-vesting/contracts/TokenVesting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,23 @@ 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 immutable 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);
Expand All @@ -51,7 +56,8 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
event BeneficiaryRemovedFromWhitelist(address indexed beneficiary);

constructor(address tokenAddress) {
// TODO: Initialize the contract
require(tokenAddress != address(0), "Invalid token address");
token = IERC20(tokenAddress);

}

Expand Down Expand Up @@ -79,21 +85,91 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
uint256 vestingDuration,
uint256 startTime
) external onlyOwner onlyWhitelisted(beneficiary) whenNotPaused {
// TODO: Implement vesting schedule creation
require(beneficiary != address(0), "Invalid beneficiary");
require(amount > 0, "Amount must be > 0");
require(vestingDuration > 0, "Vesting duration must be > 0");
require(
vestingDuration >= cliffDuration,
"Vesting duration must be >= cliff"
);
require(
vestingSchedules[beneficiary].totalAmount == 0,
"Schedule already exists"
);
require(startTime >= block.timestamp, "Start time must be in future");

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

require(
token.transferFrom(msg.sender, address(this), amount),
"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.totalAmount == 0 || schedule.revoked) {
return 0;
}

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

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

uint256 timeFromStart = block.timestamp - schedule.startTime;
uint256 vestedAmount = (schedule.totalAmount * timeFromStart) /
schedule.vestingDuration;

return vestedAmount;
}

function claimVestedTokens() external nonReentrant whenNotPaused {
// TODO: Implement token claiming
VestingSchedule storage schedule = vestingSchedules[msg.sender];

require(schedule.totalAmount > 0, "No vesting schedule found");
require(!schedule.revoked, "Vesting schedule revoked");

uint256 vestedAmount = calculateVestedAmount(msg.sender);
uint256 claimableAmount = vestedAmount - schedule.amountClaimed;

require(claimableAmount > 0, "No tokens to claim");

schedule.amountClaimed += claimableAmount;

require(token.transfer(msg.sender, claimableAmount), "Transfer failed");
emit TokensClaimed(msg.sender, 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");

uint256 vestedAmount = calculateVestedAmount(beneficiary);
uint256 unvestedAmount = schedule.totalAmount - vestedAmount;

schedule.revoked = true;

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

emit VestingRevoked(beneficiary);

}

Expand Down
100 changes: 94 additions & 6 deletions challenge-2-yield-farm/contracts/yeild.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,16 @@ contract YieldFarm is ReentrancyGuard, Ownable {
address _rewardToken,
uint256 _rewardRate
) Ownable(msg.sender) {
// TODO: Initialize contract state
require( _lpToken != address(0), "Invalid LP token address");
require( _rewardToken != address(0), "Invalid reward token address");
require( _rewardRate > 0, "Invalid reward rate");

lpToken = IERC20(_lpToken);
rewardToken = IERC20(_rewardToken);
rewardRate = _rewardRate;
lastUpdateTime = block.timestamp;
rewardPerTokenStored = 0;

}

function updateReward(address _user) internal {
Expand All @@ -90,6 +99,15 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Calculate rewards since last update
// 2. Apply boost multiplier
// 3. Return total pending rewards

if(totalStaked == 0){
return rewardPerTokenStored;
}
uint256 timeElapsed = block.timestamp - lastUpdateTime;
uint256 rewardSinceTimeElasped = (timeElapsed * rewardRate * 1e18) / totalStaked;
uint256 totalReward = rewardPerTokenStored + rewardSinceTimeElasped;
return totalReward;

}

function earned(address _user) public view returns (uint256) {
Expand All @@ -98,6 +116,15 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Calculate rewards since last update
// 2. Apply boost multiplier
// 3. Return total pending rewards
UserInfo storage user = userInfo[_user];
uint256 userRewardPerToken = rewardPerToken();
if(userRewardPerToken <= user.rewardDebt){
return user.pendingRewards;
}
uint256 rewards = ((user.amount * (userRewardPerToken - user.rewardDebt)) / 1e18);
uint256 boostMultiplier = calculateBoostMultiplier(_user);
uint256 boostedRewards = (rewards * boostMultiplier) / 100 ;
return user.pendingRewards + boostedRewards;
}

/**
Expand All @@ -111,6 +138,19 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer LP tokens from user
// 3. Update user info and total staked amount
// 4. Emit Staked event
require(_amount > 0, "Cannot stake 0");
updateReward(msg.sender);
UserInfo storage user = userInfo[msg.sender];
if(user.amount == 0){
user.startTime = block.timestamp;
}
totalStaked += _amount;
user.amount += _amount;

require(
lpToken.transferFrom(msg.sender, address(this), _amount), "Transfer failed"
);
emit Staked(msg.sender, _amount);
}

/**
Expand All @@ -124,6 +164,16 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer LP tokens to user
// 3. Update user info and total staked amount
// 4. Emit Withdrawn event
require(_amount > 0, "Cannot not withdraw 0");
UserInfo storage user = userInfo[msg.sender];
require(user.amount >= _amount, "Insufficient balance");
updateReward(msg.sender);

totalStaked -= _amount;
user.amount -= _amount;

require(lpToken.transfer(msg.sender, _amount), "Transfer failed");
emit Withdrawn(msg.sender, _amount);
}

/**
Expand All @@ -136,6 +186,18 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer rewards to user
// 3. Update user reward debt
// 4. Emit RewardsClaimed event
updateReward(msg.sender);
UserInfo storage user = userInfo[msg.sender];

uint256 reward= earned(msg.sender);
if(reward > 0){
user.pendingRewards = 0;
user.rewardDebt = (user.amount * rewardPerTokenStored) / 1e18;
require(rewardToken.transfer(msg.sender, reward), "Transfer failed");
emit RewardsClaimed(msg.sender, reward);
} else {
revert("No rewards to claim");
}
}

/**
Expand All @@ -147,6 +209,16 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Transfer all LP tokens back to user
// 2. Reset user info
// 3. Emit EmergencyWithdrawn event
UserInfo storage user = userInfo[msg.sender];
uint256 amount = user.amount;
require(amount > 0, "No LP tokens to withdraw");

user.amount = 0;
user.rewardDebt = 0;
user.pendingRewards = 0;
totalStaked -= amount;
require(lpToken.transfer(msg.sender, amount), "Transfer failed");
emit EmergencyWithdrawn(msg.sender, amount);
}

/**
Expand All @@ -161,17 +233,33 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// Requirements:
// 1. Calculate staking duration
// 2. Return appropriate multiplier based on duration thresholds
UserInfo storage user = userInfo[_user];
if(user.amount == 0) return 100;

uint256 stakingDuration = block.timestamp - user.startTime;

if(stakingDuration >= BOOST_THRESHOLD_3){
return 200;
}else if(stakingDuration >= BOOST_THRESHOLD_2){
return 150;
}
else if(stakingDuration >= BOOST_THRESHOLD_1){
return 125;
}else {
return 100;
}


}

/**
* @notice Update reward rate
* @param _newRate New reward rate per second
*/
function updateRewardRate(uint256 _newRate) external onlyOwner {
// TODO: Implement reward rate update logic
// Requirements:
// 1. Update rewards before changing rate
// 2. Set new reward rate
require(_newRate > 0, "Invalid new Rate");
updateReward(address(0));
rewardRate = _newRate;
}

/**
Expand All @@ -182,4 +270,4 @@ contract YieldFarm is ReentrancyGuard, Ownable {
function pendingRewards(address _user) external view returns (uint256) {
return earned(_user);
}
}
}
15 changes: 14 additions & 1 deletion challenge-2-yield-farm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion challenge-2-yield-farm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"hardhat": "^2.22.17"
},
"dependencies": {
"@openzeppelin/contracts": "^5.1.0"
"@openzeppelin/contracts": "^5.1.0",
"dotenv": "^16.4.7"
}
}
14 changes: 7 additions & 7 deletions challenge-3-frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ export default function Home() {
<Link href="/wallet">Wallet</Link>
</li>
<li className="mb-2">
<Link href="/send-transaction">Send transaction</Link>
<Link href="/vesting">Token Vesting</Link>
</li>
<li className="mb-2">
<li className="mb-2">
<Link href="/write-contract">Write contract</Link>
</li>
<li className="mb-2">
<Link href="/mint-redeem-lst-bifrost">Mint/Redeem LST Bifrost</Link>
</li>
<Link href="/mint-redeem-lst-bifrost">Remove Vesting Beneficiaries</Link>
</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
{/* <div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://github.com/buildstationorg/dotui"
Expand All @@ -51,7 +51,7 @@ export default function Home() {
>
Read our docs
</a>
</div>
</div> */}
</div>
<footer className="row-start-3 flex flex-col gap-4">
<div className="flex gap-6 flex-wrap items-center justify-center">
Expand Down Expand Up @@ -107,4 +107,4 @@ export default function Home() {
</footer>
</div>
);
}
}
Loading