-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathL2Reward.sol
612 lines (478 loc) · 25.9 KB
/
L2Reward.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;
import { Initializable } from "@openzeppelin-upgradeable/contracts/proxy/utils/Initializable.sol";
import { Ownable2StepUpgradeable } from "@openzeppelin-upgradeable/contracts/access/Ownable2StepUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ISemver } from "../utils/ISemver.sol";
import { IL2LockingPosition } from "../interfaces/L2/IL2LockingPosition.sol";
import { IL2Staking } from "../interfaces/L2/IL2Staking.sol";
import { IL2LiskToken } from "../interfaces/L2/IL2LiskToken.sol";
/// @title L2Reward
/// @notice This contract manages and handles L2 Staking Rewards.
contract L2Reward is Initializable, Ownable2StepUpgradeable, UUPSUpgradeable, ISemver {
/// @notice The offset value of stake weight as a liner function of remaining stake duration.
uint256 public constant OFFSET = 150;
/// @notice The default duration in days for which the rewards are added.
uint16 public constant REWARD_DURATION = 30;
/// @notice The default delay in days when adding rewards.
uint16 public constant REWARD_DURATION_DELAY = 1;
/// @notice Total of weights of all stakes for each day.
mapping(uint256 => uint256) public totalWeights;
/// @notice Total of amount expiring for each day.
mapping(uint256 => uint256) public dailyUnlockedAmounts;
/// @notice Total of rewards provided for each day.
mapping(uint256 => uint256) public dailyRewards;
/// @notice Maintains the date of the last reward date for each locking position.
mapping(uint256 => uint256) public lastClaimDate;
/// @notice Total weight of all active locking positions.
uint256 public totalWeight;
/// @notice Total amount locked from all active locking positions.
uint256 public totalAmountLocked;
/// @notice Total amount for staking positions with active locking duration (i.e. countdown is not paused)
uint256 public pendingUnlockAmount;
/// @notice Date of the last user-made action that updated the global variables.
uint256 public lastTrsDate;
/// @notice Semantic version of the contract.
string public version;
/// @notice Aggregation of surplus rewards.
uint256 public rewardsSurplus;
/// @notice Address of the staking contract.
address public stakingContract;
/// @notice Address of the locking position contract.
address public lockingPositionContract;
/// @notice Address of the L2 token contract.
address public l2TokenContract;
/// @notice Emitted when a locking position is created.
event LockingPositionCreated(uint256 indexed lockID);
/// @notice Emitted when a locking position is deleted.
event LockingPositionDeleted(uint256 indexed lockID);
/// @notice Emitted when fast unlock is initiated for a locking position.
event FastUnlockInitiated(uint256 indexed lockID);
/// @notice Emitted when locking amount is increased for a locking position.
event LockingAmountIncreased(uint256 indexed lockID, uint256 amountIncrease);
/// @notice Emitted when duration is extended against a locking position.
event LockingDurationExtended(uint256 indexed lockID, uint256 durationExtension);
/// @notice Emitted when a locking position is paused.
event LockingPositionPaused(uint256 indexed lockID);
/// @notice Emitted when a unlocking countdown is resumed against a paused position.
event UnlockingCountdownResumed(uint256 indexed lockID);
/// @notice Emitted when rewards are added.
event RewardsAdded(uint256 indexed amount, uint256 indexed duration, uint256 indexed delay);
/// @notice Emitted when the L2LiskToken contract address is changed.
event LiskTokenContractAddressChanged(address indexed oldAddress, address indexed newAddress);
/// @notice Emitted when the Stakin contract address is changed.
event StakingContractAddressChanged(address indexed oldAddress, address indexed newAddress);
/// @notice Emitted when the Locking Position contract address is changed.
event LockingPositionContractAddressChanged(address indexed oldAddress, address indexed newAddress);
/// @notice Emitted when a position is rewarded.
event RewardsClaimed(uint256 indexed lockID, uint256 amount);
/// @notice Represents duration extension for a position.
struct ExtendedDuration {
uint256 lockID;
uint256 durationExtension;
}
/// @notice Represents increased lock amount for a position.
struct IncreasedAmount {
uint256 lockID;
uint256 amountIncrease;
}
constructor() {
_disableInitializers();
}
/// @notice Ensures that only the owner can authorize a contract upgrade. It reverts if called by any address other
/// than the contract owner.
/// @param _newImplementation The address of the new contract implementation to which the proxy will be upgraded.
function _authorizeUpgrade(address _newImplementation) internal virtual override onlyOwner { }
/// @notice Initializes the contract.
/// @param _l2LiskTokenContract The address of the L2LiskToken contract.
function initialize(address _l2LiskTokenContract) public initializer {
require(_l2LiskTokenContract != address(0), "L2Reward: LSK token contract address can not be zero");
__Ownable2Step_init();
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
l2TokenContract = _l2LiskTokenContract;
lastTrsDate = todayDay();
version = "1.0.0";
emit LiskTokenContractAddressChanged(address(0x0), _l2LiskTokenContract);
}
/// @notice Updates global state against user actions.
/// @dev It is the first call in every public function.
function updateGlobalState() internal virtual {
uint256 today = todayDay();
uint256 d = lastTrsDate;
if (today <= d) return;
uint256 cappedRewards;
for (; d < today; d++) {
totalWeights[d] = totalWeight;
cappedRewards = totalAmountLocked / 365;
// capping in rewards
if (dailyRewards[d] > cappedRewards) {
rewardsSurplus += dailyRewards[d] - cappedRewards;
dailyRewards[d] = cappedRewards;
}
// update total weights due to unlockable and pending unlocks
totalWeight -= pendingUnlockAmount;
totalWeight -= OFFSET * dailyUnlockedAmounts[d + 1];
// the amount getting unlocked for a day should not be considered staked anymore
totalAmountLocked -= dailyUnlockedAmounts[d + 1];
pendingUnlockAmount -= dailyUnlockedAmounts[d + 1];
}
lastTrsDate = today;
}
/// @notice Creates a locking position.
/// @param amount Amount to be locked.
/// @param duration Duration of the locking position in days.
/// @return The ID of the newly created locking position.
function createPosition(uint256 amount, uint256 duration) public virtual returns (uint256) {
updateGlobalState();
uint256 today = todayDay();
// update total weight and amount
totalWeight += amount * (duration + OFFSET);
totalAmountLocked += amount;
dailyUnlockedAmounts[today + duration] += amount;
pendingUnlockAmount += amount;
// create a new position
// IL2LiskToken.transferFrom always returns true and reverts in case of error
// slither-disable-next-line unchecked-transfer
IL2LiskToken(l2TokenContract).transferFrom(msg.sender, address(this), amount);
// IL2LiskToken.approve always returns true and reverts in case of error
// slither-disable-next-line unused-return
IL2LiskToken(l2TokenContract).approve(stakingContract, amount);
uint256 id = IL2Staking(stakingContract).lockAmount(msg.sender, amount, duration);
lastClaimDate[id] = today;
emit LockingPositionCreated(id);
return id;
}
/// @notice Deletes multiple locking positions.
/// @param lockIDs The IDs of locking positions.
function deletePositions(uint256[] memory lockIDs) public virtual {
updateGlobalState();
for (uint8 i = 0; i < lockIDs.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(lockIDs[i]) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
_deletePosition(lockIDs[i]);
}
}
/// @notice Deletes a locking position.
/// @param lockID The ID of the locking position.
function _deletePosition(uint256 lockID) internal virtual {
// claim rewards and updates staking contract
_claimReward(lockID);
IL2Staking(stakingContract).unlock(lockID);
// lockID can be deleted only when rewards are claimed and the position is unlocked by L2Staking contract.
// slither-disable-next-line reentrancy-no-eth
delete lastClaimDate[lockID];
emit LockingPositionDeleted(lockID);
}
/// @notice Initiates fast unlock of multiple locking positions.
/// @param lockIDs The IDs of locking positions.
function initiateFastUnlock(uint256[] memory lockIDs) public virtual {
updateGlobalState();
for (uint8 i = 0; i < lockIDs.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(lockIDs[i]) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
_initiateFastUnlock(lockIDs[i]);
}
}
/// @notice Initiates a fast unlock of the locking position.
/// @param lockID The ID of the locking position.
function _initiateFastUnlock(uint256 lockID) internal virtual {
// claim rewards and inform staking contract
// slither-disable-next-line reentrancy-no-eth
_claimReward(lockID);
IL2LockingPosition.LockingPosition memory lockingPositionBeforeInitiatingFastUnlock =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
uint256 penalty = IL2Staking(stakingContract).initiateFastUnlock(lockID);
// add penalty amout to future rewards
_addRewards(penalty, REWARD_DURATION, REWARD_DURATION_DELAY);
uint256 today = todayDay();
uint256 fastUnlockDuration = IL2Staking(stakingContract).FAST_UNLOCK_DURATION();
uint256 remainingDuration;
// update global variables and arrays
if (lockingPositionBeforeInitiatingFastUnlock.pausedLockingDuration == 0) {
// removing previous expiration date
remainingDuration = lockingPositionBeforeInitiatingFastUnlock.expDate - today;
dailyUnlockedAmounts[lockingPositionBeforeInitiatingFastUnlock.expDate] -=
lockingPositionBeforeInitiatingFastUnlock.amount;
pendingUnlockAmount -= lockingPositionBeforeInitiatingFastUnlock.amount;
} else {
remainingDuration = lockingPositionBeforeInitiatingFastUnlock.pausedLockingDuration;
}
totalWeight -= (remainingDuration + OFFSET) * lockingPositionBeforeInitiatingFastUnlock.amount;
totalWeight += (fastUnlockDuration + OFFSET) * (lockingPositionBeforeInitiatingFastUnlock.amount - penalty);
// setting new expiration date
dailyUnlockedAmounts[today + fastUnlockDuration] += lockingPositionBeforeInitiatingFastUnlock.amount - penalty;
pendingUnlockAmount += lockingPositionBeforeInitiatingFastUnlock.amount - penalty;
totalAmountLocked -= penalty;
emit FastUnlockInitiated(lockID);
}
/// @notice Calculate rewards of a locking position.
/// @param lockID The ID of the locking position.
/// @return uint256 Rewards amount.
function calculateRewards(uint256 lockID) public view virtual returns (uint256) {
IL2LockingPosition.LockingPosition memory lockingPosition =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
uint256 today = todayDay();
uint256 rewardableDuration;
uint256 lastRewardDay;
uint256 weight;
if (lockingPosition.pausedLockingDuration == 0) {
// If rewards are claimed against an expired position that was already claimed after expiry period,
// rewardableDuration is zero
// If lastClaimDate for lockID is greater than expiry date, Math.trySub sets rewardableDuration to zero
// and the overflow flag can be ignored.
// slither-disable-next-line unused-return
(, rewardableDuration) = Math.trySub(lockingPosition.expDate, lastClaimDate[lockID]);
lastRewardDay = Math.min(lockingPosition.expDate, today);
} else {
rewardableDuration = lockingPosition.pausedLockingDuration;
lastRewardDay = today;
}
uint256 reward = 0;
if (rewardableDuration > 0) {
weight = lockingPosition.amount * (rewardableDuration + OFFSET);
} else {
return reward;
}
for (uint256 d = lastClaimDate[lockID]; d < lastRewardDay; d++) {
reward += (weight * dailyRewards[d]) / totalWeights[d];
if (lockingPosition.pausedLockingDuration == 0) {
// unlocking period is active, weight is decreasing
weight -= lockingPosition.amount;
}
}
return reward;
}
/// @notice Claim rewards against multiple locking position.
/// @param lockIDs The IDs of locking position.
function claimRewards(uint256[] memory lockIDs) public virtual {
updateGlobalState();
for (uint8 i = 0; i < lockIDs.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(lockIDs[i]) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
_claimReward(lockIDs[i]);
}
}
/// @notice Claim rewards against a locking position.
/// @param lockID The ID of the locking position.
function _claimReward(uint256 lockID) internal virtual {
require(lastClaimDate[lockID] != 0, "L2Reward: Locking position does not exist");
uint256 today = todayDay();
uint256 reward;
if (lastClaimDate[lockID] >= today) {
emit RewardsClaimed(lockID, 0);
return;
}
reward = calculateRewards(lockID);
emit RewardsClaimed(lockID, reward);
lastClaimDate[lockID] = today;
if (reward != 0) {
// IL2LiskToken.transfer always returns true and reverts in case of error
// slither-disable-next-line unchecked-transfer
IL2LiskToken(l2TokenContract).transfer(msg.sender, reward);
}
}
/// @notice Increases locked amount against multiple locking positions.
/// @param locks The IDs of locking positions.
function increaseLockingAmount(IncreasedAmount[] memory locks) public virtual {
updateGlobalState();
for (uint8 i = 0; i < locks.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(locks[i].lockID) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
require(locks[i].amountIncrease > 0, "L2Reward: Increased amount should be greater than zero");
_increaseLockingAmount(locks[i].lockID, locks[i].amountIncrease);
}
}
/// @notice Increases locked amount against a locking position.
/// @param lockID The ID of the locking position.
/// @param amountIncrease The amount to be increased.
function _increaseLockingAmount(uint256 lockID, uint256 amountIncrease) internal virtual {
// claim rewards and update staking contract
_claimReward(lockID);
// IL2LiskToken.transferFrom always returns true and reverts in case of error
// slither-disable-next-line unchecked-transfer
IL2LiskToken(l2TokenContract).transferFrom(msg.sender, address(this), amountIncrease);
// IL2LiskToken.approve always returns true and reverts in case of error
// slither-disable-next-line unused-return
IL2LiskToken(l2TokenContract).approve(stakingContract, amountIncrease);
IL2Staking(stakingContract).increaseLockingAmount(lockID, amountIncrease);
IL2LockingPosition.LockingPosition memory lockingPosition =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
uint256 today = todayDay();
// update globals
totalAmountLocked += amountIncrease;
if (lockingPosition.pausedLockingDuration == 0) {
// duration for active position => lockingPosition.expDate - today;
totalWeight += amountIncrease * (lockingPosition.expDate - today + OFFSET);
dailyUnlockedAmounts[lockingPosition.expDate] += amountIncrease;
pendingUnlockAmount += amountIncrease;
} else {
// duration for paused position => lockingPosition.pausedLockingDuration
totalWeight += amountIncrease * (lockingPosition.pausedLockingDuration + OFFSET);
}
emit LockingAmountIncreased(lockID, amountIncrease);
}
/// @notice Extends duration of multiple locking positions.
/// @param locks The IDs of locking positions and their extended duration.
function extendDuration(ExtendedDuration[] memory locks) public virtual {
updateGlobalState();
for (uint8 i = 0; i < locks.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(locks[i].lockID) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
require(locks[i].durationExtension > 0, "L2Reward: Extended duration should be greater than zero");
_extendDuration(locks[i].lockID, locks[i].durationExtension);
}
}
/// @notice Extends duration of a locking position.
/// @param lockID The ID of the locking position.
/// @param durationExtension The duration to be extended in days.
function _extendDuration(uint256 lockID, uint256 durationExtension) internal virtual {
IL2LockingPosition.LockingPosition memory lockingPosition =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
// claim rewards and update staking contract
_claimReward(lockID);
IL2Staking(stakingContract).extendLockingDuration(lockID, durationExtension);
// update globals
totalWeight += lockingPosition.amount * durationExtension;
if (lockingPosition.pausedLockingDuration == 0) {
// locking period has not finished
if (lockingPosition.expDate > todayDay()) {
dailyUnlockedAmounts[lockingPosition.expDate] -= lockingPosition.amount;
dailyUnlockedAmounts[lockingPosition.expDate + durationExtension] += lockingPosition.amount;
}
// locking period has expired, re-lock amount and assume that expiry date is today
else {
dailyUnlockedAmounts[todayDay() + durationExtension] += lockingPosition.amount;
totalAmountLocked += lockingPosition.amount;
pendingUnlockAmount += lockingPosition.amount;
totalWeight += lockingPosition.amount * OFFSET;
}
}
emit LockingDurationExtended(lockID, durationExtension);
}
/// @notice Pauses unlocking of multiple locking positions.
/// @param lockIDs The IDs of locking positions.
function pauseUnlocking(uint256[] memory lockIDs) public virtual {
updateGlobalState();
for (uint8 i = 0; i < lockIDs.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(lockIDs[i]) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
_pauseUnlocking(lockIDs[i]);
}
}
/// @notice Pauses unlocking of a locking position.
/// @param lockID The ID of the locking position.
function _pauseUnlocking(uint256 lockID) internal virtual {
// claim rewards and update staking contract
_claimReward(lockID);
IL2Staking(stakingContract).pauseRemainingLockingDuration(lockID);
IL2LockingPosition.LockingPosition memory lockingPosition =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
// update globals
pendingUnlockAmount -= lockingPosition.amount;
dailyUnlockedAmounts[lockingPosition.expDate] -= lockingPosition.amount;
emit LockingPositionPaused(lockID);
}
/// @notice Resumes unlocking of multiple locking positions.
/// @param lockIDs The IDs of locking positions.
function resumeUnlockingCountdown(uint256[] memory lockIDs) public virtual {
updateGlobalState();
for (uint8 i = 0; i < lockIDs.length; i++) {
require(
IL2LockingPosition(lockingPositionContract).ownerOf(lockIDs[i]) == msg.sender,
"L2Reward: msg.sender does not own the locking position"
);
_resumeUnlockingCountdown(lockIDs[i]);
}
}
/// @notice Resumes unlocking of a locking position.
/// @param lockID The ID of the locking position.
function _resumeUnlockingCountdown(uint256 lockID) internal virtual {
// claim rewards and update staking contract
_claimReward(lockID);
IL2Staking(stakingContract).resumeCountdown(lockID);
IL2LockingPosition.LockingPosition memory lockingPosition =
IL2LockingPosition(lockingPositionContract).getLockingPosition(lockID);
// update globals
pendingUnlockAmount += lockingPosition.amount;
dailyUnlockedAmounts[lockingPosition.expDate] += lockingPosition.amount;
emit UnlockingCountdownResumed(lockID);
}
/// @notice Adds daily rewards between provided duration.
/// @param amount Amount to be added to daily rewards.
/// @param duration Duration in days for which the daily rewards is to be added.
/// @param delay Determines the start day from today till duration for whom rewards should be added.
function _addRewards(uint256 amount, uint16 duration, uint16 delay) internal virtual {
require(amount > 0, "L2Reward: Funded amount should be greater than zero");
require(duration > 0, "L2Reward: Funding duration should be greater than zero");
require(delay > 0, "L2Reward: Funding should start from next day or later");
uint256 dailyReward = amount / duration;
uint256 today = todayDay();
uint256 endDate = today + delay + duration;
for (uint256 d = today + delay; d < endDate; d++) {
dailyRewards[d] += dailyReward;
}
}
/// @notice Redistribute unused rewards.
/// @param amount Amount to be added to daily rewards.
/// @param duration Duration in days for which the daily rewards is to be added.
/// @param delay Determines the start day from today till duration from which rewards should be added.
function addUnusedRewards(uint256 amount, uint16 duration, uint16 delay) public virtual onlyOwner {
require(amount > 0, "L2Reward: Funded amount should be greater than zero");
require(duration > 0, "L2Reward: Funding duration should be greater than zero");
require(delay > 0, "L2Reward: Rewards can only be added from next day or later");
require(amount <= rewardsSurplus, "L2Reward: Reward amount should not exceed available surplus funds");
_addRewards(amount, duration, delay);
rewardsSurplus -= amount;
emit RewardsAdded(amount, duration, delay);
}
/// @notice Adds new daily rewards between provided duration.
/// @param amount Amount to be added to daily rewards.
/// @param duration Duration in days for which the daily rewards is to be added.
/// @param delay Determines the start day from today till duration for which rewards should be added.
function fundStakingRewards(uint256 amount, uint16 duration, uint16 delay) public virtual onlyOwner {
require(amount > 0, "L2Reward: Funded amount should be greater than zero");
require(duration > 0, "L2Reward: Funding duration should be greater than zero");
require(delay > 0, "L2Reward: Funding should start from next day or later");
// IL2LiskToken.transferFrom always returns true and reverts in case of error
// slither-disable-next-line unchecked-transfer
IL2LiskToken(l2TokenContract).transferFrom(msg.sender, address(this), amount);
_addRewards(amount, duration, delay);
emit RewardsAdded(amount, duration, delay);
}
/// @notice Initializes the LockingPosition address.
/// @param _lockingPositionContract Address of the locking position contract.
function initializeLockingPosition(address _lockingPositionContract) public onlyOwner {
require(lockingPositionContract == address(0), "L2Reward: LockingPosition contract is already initialized");
require(_lockingPositionContract != address(0), "L2Reward: LockingPosition contract address can not be zero");
lockingPositionContract = _lockingPositionContract;
emit LockingPositionContractAddressChanged(address(0x0), lockingPositionContract);
}
/// @notice Initializes the L2Staking address.
/// @param _stakingContract Address of the staking contract.
function initializeStaking(address _stakingContract) public onlyOwner {
require(stakingContract == address(0), "L2Reward: Staking contract is already initialized");
require(_stakingContract != address(0), "L2Reward: Staking contract address can not be zero");
stakingContract = _stakingContract;
emit StakingContractAddressChanged(address(0x0), stakingContract);
}
/// @notice Returns the current day.
/// @return The current day.
function todayDay() public view virtual returns (uint256) {
return block.timestamp / 1 days;
}
}