@@ -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