Skip to content

Commit 019aa19

Browse files
committed
Implement YieldFarm logic with staking, rewards, boosts and remove TODO prefixes.
1 parent 8671bf2 commit 019aa19

File tree

1 file changed

+100
-11
lines changed

1 file changed

+100
-11
lines changed

challenge-2-yield-farm/contracts/yeild.sol

Lines changed: 100 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ contract YieldFarm is ReentrancyGuard, Ownable {
5757
event RewardsClaimed(address indexed user, uint256 amount);
5858
event EmergencyWithdrawn(address indexed user, uint256 amount);
5959

60-
// TODO: Implement the following functions
60+
// Implement the following functions
6161

6262
/**
6363
* @notice Initialize the contract with the LP token and reward token addresses
@@ -70,9 +70,17 @@ contract YieldFarm is ReentrancyGuard, Ownable {
7070
address _rewardToken,
7171
uint256 _rewardRate
7272
) Ownable(msg.sender) {
73-
// TODO: Initialize contract state
73+
// Initialize contract state
74+
require(_lpToken != address(0) && _rewardToken != address(0), "Zero address");
75+
lpToken = IERC20(_lpToken);
76+
rewardToken = IERC20(_rewardToken);
77+
rewardRate = _rewardRate;
78+
lastUpdateTime = block.timestamp;
7479
}
7580

81+
/**
82+
* @dev Internal function to update global and user-specific reward accounting
83+
*/
7684
function updateReward(address _user) internal {
7785
rewardPerTokenStored = rewardPerToken();
7886
lastUpdateTime = block.timestamp;
@@ -85,68 +93,132 @@ contract YieldFarm is ReentrancyGuard, Ownable {
8593
}
8694

8795
function rewardPerToken() public view returns (uint256) {
88-
// TODO: Implement pending rewards calculation
96+
// Implement pending rewards calculation
8997
// Requirements:
9098
// 1. Calculate rewards since last update
9199
// 2. Apply boost multiplier
92100
// 3. Return total pending rewards
101+
if (totalStaked == 0) {
102+
return rewardPerTokenStored;
103+
}
104+
uint256 timeElapsed = block.timestamp - lastUpdateTime;
105+
uint256 newRewards = (timeElapsed * rewardRate * 1e18) / totalStaked;
106+
return rewardPerTokenStored + newRewards;
93107
}
94108

95109
function earned(address _user) public view returns (uint256) {
96-
// TODO: Implement pending rewards calculation
110+
// Implement pending rewards calculation
97111
// Requirements:
98112
// 1. Calculate rewards since last update
99113
// 2. Apply boost multiplier
100114
// 3. Return total pending rewards
115+
UserInfo storage user = userInfo[_user];
116+
uint256 accumulated = (user.amount * rewardPerToken()) / 1e18;
117+
uint256 pending = accumulated - user.rewardDebt + user.pendingRewards;
118+
uint256 boost = calculateBoostMultiplier(_user);
119+
return (pending * boost) / 100;
101120
}
102121

103122
/**
104123
* @notice Stake LP tokens into the farm
105124
* @param _amount Amount of LP tokens to stake
106125
*/
107126
function stake(uint256 _amount) external nonReentrant {
108-
// TODO: Implement staking logic
127+
// Implement staking logic
109128
// Requirements:
110129
// 1. Update rewards
111130
// 2. Transfer LP tokens from user
112131
// 3. Update user info and total staked amount
113132
// 4. Emit Staked event
133+
require(_amount > 0, "Cannot stake 0");
134+
updateReward(msg.sender);
135+
136+
lpToken.transferFrom(msg.sender, address(this), _amount);
137+
138+
UserInfo storage user = userInfo[msg.sender];
139+
user.amount += _amount;
140+
totalStaked += _amount;
141+
142+
if (user.startTime == 0) {
143+
user.startTime = block.timestamp;
144+
}
145+
146+
emit Staked(msg.sender, _amount);
114147
}
115148

116149
/**
117150
* @notice Withdraw staked LP tokens
118151
* @param _amount Amount of LP tokens to withdraw
119152
*/
120153
function withdraw(uint256 _amount) external nonReentrant {
121-
// TODO: Implement withdrawal logic
154+
// Implement withdrawal logic
122155
// Requirements:
123156
// 1. Update rewards
124157
// 2. Transfer LP tokens to user
125158
// 3. Update user info and total staked amount
126159
// 4. Emit Withdrawn event
160+
require(_amount > 0, "Cannot withdraw 0");
161+
UserInfo storage user = userInfo[msg.sender];
162+
require(user.amount >= _amount, "Insufficient balance");
163+
164+
updateReward(msg.sender);
165+
166+
user.amount -= _amount;
167+
totalStaked -= _amount;
168+
lpToken.transfer(msg.sender, _amount);
169+
170+
if (user.amount == 0) {
171+
user.startTime = 0;
172+
}
173+
174+
emit Withdrawn(msg.sender, _amount);
127175
}
128176

129177
/**
130178
* @notice Claim pending rewards
131179
*/
132180
function claimRewards() external nonReentrant {
133-
// TODO: Implement reward claiming logic
181+
// Implement reward claiming logic
134182
// Requirements:
135183
// 1. Calculate pending rewards with boost multiplier
136184
// 2. Transfer rewards to user
137185
// 3. Update user reward debt
138186
// 4. Emit RewardsClaimed event
187+
updateReward(msg.sender);
188+
189+
UserInfo storage user = userInfo[msg.sender];
190+
uint256 reward = user.pendingRewards;
191+
require(reward > 0, "No rewards");
192+
193+
user.pendingRewards = 0;
194+
rewardToken.transfer(msg.sender, reward);
195+
196+
emit RewardsClaimed(msg.sender, reward);
139197
}
140198

141199
/**
142200
* @notice Emergency withdraw without caring about rewards
143201
*/
144202
function emergencyWithdraw() external nonReentrant {
145-
// TODO: Implement emergency withdrawal
203+
// Implement emergency withdrawal
146204
// Requirements:
147205
// 1. Transfer all LP tokens back to user
148206
// 2. Reset user info
149207
// 3. Emit EmergencyWithdrawn event
208+
UserInfo storage user = userInfo[msg.sender];
209+
uint256 amount = user.amount;
210+
require(amount > 0, "Nothing to withdraw");
211+
212+
totalStaked -= amount;
213+
214+
user.amount = 0;
215+
user.rewardDebt = 0;
216+
user.pendingRewards = 0;
217+
user.startTime = 0;
218+
219+
lpToken.transfer(msg.sender, amount);
220+
221+
emit EmergencyWithdrawn(msg.sender, amount);
150222
}
151223

152224
/**
@@ -157,21 +229,38 @@ contract YieldFarm is ReentrancyGuard, Ownable {
157229
function calculateBoostMultiplier(
158230
address _user
159231
) public view returns (uint256) {
160-
// TODO: Implement boost multiplier calculation
232+
// Implement boost multiplier calculation
161233
// Requirements:
162234
// 1. Calculate staking duration
163235
// 2. Return appropriate multiplier based on duration thresholds
236+
UserInfo storage user = userInfo[_user];
237+
if (user.startTime == 0) {
238+
return 100;
239+
}
240+
241+
uint256 duration = block.timestamp - user.startTime;
242+
243+
if (duration >= BOOST_THRESHOLD_3) {
244+
return 200;
245+
} else if (duration >= BOOST_THRESHOLD_2) {
246+
return 150;
247+
} else if (duration >= BOOST_THRESHOLD_1) {
248+
return 125;
249+
}
250+
return 100;
164251
}
165252

166253
/**
167254
* @notice Update reward rate
168255
* @param _newRate New reward rate per second
169256
*/
170257
function updateRewardRate(uint256 _newRate) external onlyOwner {
171-
// TODO: Implement reward rate update logic
258+
// Implement reward rate update logic
172259
// Requirements:
173260
// 1. Update rewards before changing rate
174261
// 2. Set new reward rate
262+
updateReward(address(0));
263+
rewardRate = _newRate;
175264
}
176265

177266
/**
@@ -182,4 +271,4 @@ contract YieldFarm is ReentrancyGuard, Ownable {
182271
function pendingRewards(address _user) external view returns (uint256) {
183272
return earned(_user);
184273
}
185-
}
274+
}

0 commit comments

Comments
 (0)