generated from PaulRBerg/hardhat-template
/
StashRewardDistro.sol
130 lines (105 loc) · 4.73 KB
/
StashRewardDistro.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
import { IBooster } from "../interfaces/IBooster.sol";
import { IStashRewardDistro } from "../interfaces/IStashRewardDistro.sol";
import { IVirtualRewards } from "../interfaces/IVirtualRewards.sol";
import { IExtraRewardStash } from "../interfaces/IExtraRewardStash.sol";
import { AuraMath } from "../utils/AuraMath.sol";
contract StashRewardDistro is IStashRewardDistro {
using AuraMath for uint256;
using SafeERC20 for IERC20;
/* -------------------------------------------------------------------
Storage
------------------------------------------------------------------- */
// @dev Epoch duration
uint256 public constant EPOCH_DURATION = 1 weeks;
// @dev The booster address
IBooster public immutable booster;
// @dev Epoch => Pool ID => Token => Amount
mapping(uint256 => mapping(uint256 => mapping(address => uint256))) public getFunds;
/* -------------------------------------------------------------------
Events
------------------------------------------------------------------- */
event Funded(uint256 epoch, uint256 pid, address token, uint256 amount);
/* -------------------------------------------------------------------
Constructor
------------------------------------------------------------------- */
constructor(address _booster) {
booster = IBooster(_booster);
}
/* -------------------------------------------------------------------
View
------------------------------------------------------------------- */
function getCurrentEpoch() external view returns (uint256) {
return _getCurrentEpoch();
}
/* -------------------------------------------------------------------
Core
------------------------------------------------------------------- */
function fundPool(
uint256 _pid,
address _token,
uint256 _amount,
uint256 _periods
) external {
// Keep 1 wei of the reward token for each period to faciliate
// processing idle rewards if needed
uint256 rewardAmount = _amount - _periods;
// Loop through n periods and assign rewards to each epoch
uint256 epoch = _getCurrentEpoch();
uint256 epochAmount = rewardAmount.div(_periods);
for (uint256 i = 0; i < _periods; i++) {
getFunds[epoch][_pid][_token] = getFunds[epoch][_pid][_token].add(epochAmount);
emit Funded(epoch, _pid, _token, epochAmount);
epoch++;
}
IERC20(_token).transferFrom(msg.sender, address(this), _amount);
}
function queueRewards(uint256 _pid, address _token) external {
_queueRewards(_getCurrentEpoch(), _pid, _token);
}
function queueRewards(
uint256 _pid,
address _token,
uint256 _epoch
) external {
require(_epoch <= _getCurrentEpoch(), "!epoch");
_queueRewards(_epoch, _pid, _token);
}
function processIdleRewards(uint256 _pid, address _token) external {
// Get the stash and the extra rewards contract
IBooster.PoolInfo memory poolInfo = booster.poolInfo(_pid);
IExtraRewardStash stash = IExtraRewardStash(poolInfo.stash);
(, address rewards, ) = stash.tokenInfo(_token);
// Check that the period finish has passed and there are queued
// rewards that need processing
uint256 periodFinish = IVirtualRewards(rewards).periodFinish();
uint256 queuedRewards = IVirtualRewards(rewards).queuedRewards();
require(block.timestamp > periodFinish, "!periodFinish");
require(queuedRewards != 0, "!queueRewards");
// Transfer 1 wei to the stash and call earmark to force a new
// queue of rewards to start
IERC20(_token).safeTransfer(address(stash), 1);
booster.earmarkRewards(_pid);
}
/* -------------------------------------------------------------------
Internal
------------------------------------------------------------------- */
function _queueRewards(
uint256 _epoch,
uint256 _pid,
address _token
) internal {
uint256 amount = getFunds[_epoch][_pid][_token];
require(amount != 0, "!amount");
getFunds[_epoch][_pid][_token] = 0;
IBooster.PoolInfo memory poolInfo = booster.poolInfo(_pid);
IERC20(_token).safeTransfer(poolInfo.stash, amount);
booster.earmarkRewards(_pid);
}
function _getCurrentEpoch() internal view returns (uint256) {
return block.timestamp.div(EPOCH_DURATION).mul(EPOCH_DURATION);
}
}