@@ -4,23 +4,22 @@ pragma solidity ^0.8.26;
4
4
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
5
5
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol " ;
6
6
7
+ import { TrustedCodehashAccess } from "./access/TrustedCodehashAccess.sol " ;
8
+ import { IStakeManager } from "./IStakeManager.sol " ;
9
+
7
10
// Rewards Streamer with Multiplier Points
8
- contract RewardsStreamerMP is ReentrancyGuard {
9
- error StakingManager__AmountCannotBeZero ();
10
- error StakingManager__TransferFailed ();
11
- error StakingManager__InsufficientBalance ();
12
- error StakingManager__InvalidLockingPeriod ();
13
- error StakingManager__CannotRestakeWithLockedFunds ();
14
- error StakingManager__TokensAreLocked ();
15
-
16
- IERC20 public immutable STAKING_TOKEN;
11
+ contract RewardsStreamerMP is IStakeManager , TrustedCodehashAccess , ReentrancyGuard {
12
+ error StakeManager__TransferFailed ();
13
+ error StakeManager__CannotRestakeWithLockedFunds ();
14
+
15
+ IERC20 public immutable STAKE_TOKEN;
17
16
IERC20 public immutable REWARD_TOKEN;
18
17
19
18
uint256 public constant SCALE_FACTOR = 1e18 ;
20
- uint256 public constant MP_RATE_PER_YEAR = 1e18 ;
19
+ uint256 public constant MP_APY = 1e18 ;
21
20
22
- uint256 public constant MIN_LOCKING_PERIOD = 90 days ;
23
- uint256 public constant MAX_LOCKING_PERIOD = 4 * 365 days ;
21
+ uint256 public constant MIN_LOCKUP_PERIOD = 90 days ;
22
+ uint256 public constant MAX_LOCKUP_PERIOD = 4 * 365 days ;
24
23
uint256 public constant MAX_MULTIPLIER = 4 ;
25
24
26
25
uint256 public totalStaked;
@@ -42,50 +41,50 @@ contract RewardsStreamerMP is ReentrancyGuard {
42
41
mapping (address account = > UserInfo data ) public users;
43
42
44
43
constructor (address _stakingToken , address _rewardToken ) {
45
- STAKING_TOKEN = IERC20 (_stakingToken);
44
+ STAKE_TOKEN = IERC20 (_stakingToken);
46
45
REWARD_TOKEN = IERC20 (_rewardToken);
47
46
lastMPUpdatedTime = block .timestamp ;
48
47
}
49
48
50
- function stake (uint256 amount , uint256 lockPeriod ) external nonReentrant {
51
- if (amount == 0 ) {
52
- revert StakingManager__AmountCannotBeZero ();
49
+ function stake (uint256 _amount , uint256 _seconds ) external onlyTrustedCodehash nonReentrant {
50
+ if (_amount == 0 ) {
51
+ revert StakeManager__StakeIsTooLow ();
53
52
}
54
53
55
- if (lockPeriod != 0 && (lockPeriod < MIN_LOCKING_PERIOD || lockPeriod > MAX_LOCKING_PERIOD )) {
56
- revert StakingManager__InvalidLockingPeriod ();
54
+ if (_seconds != 0 && (_seconds < MIN_LOCKUP_PERIOD || _seconds > MAX_LOCKUP_PERIOD )) {
55
+ revert StakeManager__InvalidLockTime ();
57
56
}
58
57
59
58
_updateGlobalState ();
60
59
updateUserMP (msg .sender );
61
60
62
61
UserInfo storage user = users[msg .sender ];
63
62
if (user.lockUntil != 0 && user.lockUntil > block .timestamp ) {
64
- revert StakingManager__CannotRestakeWithLockedFunds ();
63
+ revert StakeManager__CannotRestakeWithLockedFunds ();
65
64
}
66
65
67
66
uint256 userRewards = calculateUserRewards (msg .sender );
68
67
if (userRewards > 0 ) {
69
68
distributeRewards (msg .sender , userRewards);
70
69
}
71
70
72
- bool success = STAKING_TOKEN .transferFrom (msg .sender , address (this ), amount );
71
+ bool success = STAKE_TOKEN .transferFrom (msg .sender , address (this ), _amount );
73
72
if (! success) {
74
- revert StakingManager__TransferFailed ();
73
+ revert StakeManager__TransferFailed ();
75
74
}
76
75
77
- user.stakedBalance += amount ;
78
- totalStaked += amount ;
76
+ user.stakedBalance += _amount ;
77
+ totalStaked += _amount ;
79
78
80
- uint256 initialMP = amount ;
81
- uint256 userPotentialMP = amount * MAX_MULTIPLIER;
79
+ uint256 initialMP = _amount ;
80
+ uint256 userPotentialMP = _amount * MAX_MULTIPLIER;
82
81
83
- if (lockPeriod != 0 ) {
84
- uint256 lockMultiplier = (lockPeriod * MAX_MULTIPLIER * SCALE_FACTOR) / MAX_LOCKING_PERIOD ;
82
+ if (_seconds != 0 ) {
83
+ uint256 lockMultiplier = (_seconds * MAX_MULTIPLIER * SCALE_FACTOR) / MAX_LOCKUP_PERIOD ;
85
84
lockMultiplier = lockMultiplier / SCALE_FACTOR;
86
- initialMP += (amount * lockMultiplier);
87
- userPotentialMP += (amount * lockMultiplier);
88
- user.lockUntil = block .timestamp + lockPeriod ;
85
+ initialMP += (_amount * lockMultiplier);
86
+ userPotentialMP += (_amount * lockMultiplier);
87
+ user.lockUntil = block .timestamp + _seconds ;
89
88
} else {
90
89
user.lockUntil = 0 ;
91
90
}
@@ -100,14 +99,14 @@ contract RewardsStreamerMP is ReentrancyGuard {
100
99
user.lastMPUpdateTime = block .timestamp ;
101
100
}
102
101
103
- function unstake (uint256 amount ) external nonReentrant {
102
+ function unstake (uint256 _amount ) external onlyTrustedCodehash nonReentrant {
104
103
UserInfo storage user = users[msg .sender ];
105
- if (amount > user.stakedBalance) {
106
- revert StakingManager__InsufficientBalance ();
104
+ if (_amount > user.stakedBalance) {
105
+ revert StakeManager__InsufficientFunds ();
107
106
}
108
107
109
108
if (block .timestamp < user.lockUntil) {
110
- revert StakingManager__TokensAreLocked ();
109
+ revert StakeManager__FundsLocked ();
111
110
}
112
111
113
112
_updateGlobalState ();
@@ -119,10 +118,10 @@ contract RewardsStreamerMP is ReentrancyGuard {
119
118
}
120
119
121
120
uint256 previousStakedBalance = user.stakedBalance;
122
- user.stakedBalance -= amount ;
123
- totalStaked -= amount ;
121
+ user.stakedBalance -= _amount ;
122
+ totalStaked -= _amount ;
124
123
125
- uint256 amountRatio = (amount * SCALE_FACTOR) / previousStakedBalance;
124
+ uint256 amountRatio = (_amount * SCALE_FACTOR) / previousStakedBalance;
126
125
uint256 mpToReduce = (user.userMP * amountRatio) / SCALE_FACTOR;
127
126
uint256 potentialMPToReduce = (user.userPotentialMP * amountRatio) / SCALE_FACTOR;
128
127
@@ -131,14 +130,37 @@ contract RewardsStreamerMP is ReentrancyGuard {
131
130
totalMP -= mpToReduce;
132
131
potentialMP -= potentialMPToReduce;
133
132
134
- bool success = STAKING_TOKEN .transfer (msg .sender , amount );
133
+ bool success = STAKE_TOKEN .transfer (msg .sender , _amount );
135
134
if (! success) {
136
- revert StakingManager__TransferFailed ();
135
+ revert StakeManager__TransferFailed ();
137
136
}
138
137
139
138
user.userRewardIndex = rewardIndex;
140
139
}
141
140
141
+ function lock (uint256 _secondsIncrease ) external onlyTrustedCodehash {
142
+ //TODO: increase lock time
143
+ revert ("Not implemented " );
144
+ }
145
+
146
+ function exit () external returns (bool _leaveAccepted ) {
147
+ if (! isTrustedCodehash (msg .sender .codehash)) {
148
+ //case owner removed access from a class of StakeVault,. they might exit
149
+ delete user[msg .sender ];
150
+ return true ;
151
+ } else {
152
+ //TODO: handle other cases
153
+ //TODO: handle update/migration case
154
+ //TODO: handle emergency exit
155
+ revert ("Not implemented " );
156
+ }
157
+ }
158
+
159
+ function acceptUpdate () external onlyTrustedCodehash returns (address _migrated ) {
160
+ //TODO: handle update/migration
161
+ revert ("Not implemented " );
162
+ }
163
+
142
164
function _updateGlobalState () internal {
143
165
updateGlobalMP ();
144
166
updateRewardIndex ();
@@ -160,7 +182,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
160
182
return ;
161
183
}
162
184
163
- uint256 accruedMP = (timeDiff * totalStaked * MP_RATE_PER_YEAR) / ( 365 days * SCALE_FACTOR );
185
+ uint256 accruedMP = calculateMP ( totalStaked, timeDiff );
164
186
if (accruedMP > potentialMP) {
165
187
accruedMP = potentialMP;
166
188
}
@@ -193,8 +215,8 @@ contract RewardsStreamerMP is ReentrancyGuard {
193
215
}
194
216
}
195
217
196
- function updateUserMP (address userAddress ) internal {
197
- UserInfo storage user = users[userAddress ];
218
+ function updateUserMP (address _vault ) internal {
219
+ UserInfo storage user = users[_vault ];
198
220
199
221
if (user.userPotentialMP == 0 || user.stakedBalance == 0 ) {
200
222
user.lastMPUpdateTime = block .timestamp ;
@@ -206,7 +228,7 @@ contract RewardsStreamerMP is ReentrancyGuard {
206
228
return ;
207
229
}
208
230
209
- uint256 accruedMP = (timeDiff * user.stakedBalance * MP_RATE_PER_YEAR) / ( 365 days * SCALE_FACTOR );
231
+ uint256 accruedMP = calculateMP ( user.stakedBalance, timeDiff );
210
232
211
233
if (accruedMP > user.userPotentialMP) {
212
234
accruedMP = user.userPotentialMP;
@@ -218,8 +240,8 @@ contract RewardsStreamerMP is ReentrancyGuard {
218
240
user.lastMPUpdateTime = block .timestamp ;
219
241
}
220
242
221
- function calculateUserRewards (address userAddress ) public view returns (uint256 ) {
222
- UserInfo storage user = users[userAddress ];
243
+ function calculateUserRewards (address _vault ) public view returns (uint256 ) {
244
+ UserInfo storage user = users[_vault ];
223
245
uint256 userWeight = user.stakedBalance + user.userMP;
224
246
uint256 deltaRewardIndex = rewardIndex - user.userRewardIndex;
225
247
return (userWeight * deltaRewardIndex) / SCALE_FACTOR;
@@ -236,19 +258,35 @@ contract RewardsStreamerMP is ReentrancyGuard {
236
258
237
259
bool success = REWARD_TOKEN.transfer (to, amount);
238
260
if (! success) {
239
- revert StakingManager__TransferFailed ();
261
+ revert StakeManager__TransferFailed ();
240
262
}
241
263
}
242
264
243
- function getStakedBalance (address userAddress ) external view returns (uint256 ) {
244
- return users[userAddress].stakedBalance;
265
+ function calculateMP (uint256 _balance , uint256 _deltaTime ) public pure returns (uint256 ) {
266
+ return (_deltaTime * _balance * MP_APY) / (365 days * SCALE_FACTOR);
267
+ }
268
+
269
+ function getStakedBalance (address _vault ) external view returns (uint256 _balance ) {
270
+ return users[_vault].stakedBalance;
271
+ }
272
+
273
+ function getPendingRewards (address _vault ) external view returns (uint256 ) {
274
+ return calculateUserRewards (_vault);
275
+ }
276
+
277
+ function getUserInfo (address _vault ) external view returns (UserInfo memory ) {
278
+ return users[_vault];
279
+ }
280
+
281
+ function totalSupplyMinted () external view returns (uint256 _totalSupply ) {
282
+ return totalStaked + totalMP;
245
283
}
246
284
247
- function getPendingRewards ( address userAddress ) external view returns (uint256 ) {
248
- return calculateUserRewards (userAddress) ;
285
+ function totalSupply ( ) external view returns (uint256 _totalSupply ) {
286
+ return totalStaked + totalMP + potentialMP ;
249
287
}
250
288
251
- function getUserInfo ( address userAddress ) external view returns (UserInfo memory ) {
252
- return users[userAddress] ;
289
+ function pendingReward ( ) external view returns (uint256 _pendingReward ) {
290
+ return STAKE_TOKEN (). balanceOf ( address ( this )) - accountedRewards ;
253
291
}
254
292
}
0 commit comments