-
Notifications
You must be signed in to change notification settings - Fork 2
/
StakingRewardsFactory.sol
162 lines (143 loc) · 5.25 KB
/
StakingRewardsFactory.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
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.11;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./StakingRewards.sol";
contract StakingRewardsFactory is Ownable {
// immutables
uint256 public stakingRewardsGenesis;
// the staking tokens for which the rewards contract has been deployed
address[] public stakingTokens;
// info about rewards for a particular staking token
struct StakingRewardsInfo {
address stakingRewards;
address[] poolRewardToken;
uint256[] poolRewardAmount;
}
// multiple reward tokens
//address[] public rewardTokens;
// rewards info by staking token
mapping(address => StakingRewardsInfo)
public stakingRewardsInfoByStakingToken;
constructor(uint256 _stakingRewardsGenesis) public Ownable() {
require(
_stakingRewardsGenesis >= block.timestamp,
"StakingRewardsFactory::constructor: genesis too soon"
);
stakingRewardsGenesis = _stakingRewardsGenesis;
}
///// permissioned functions
// deploy a staking reward contract for the staking token, and store the reward amount
// the reward will be distributed to the staking reward contract no sooner than the genesis
function deploy(
address stakingToken,
address[] memory rewardTokens,
uint256[] memory rewardAmounts
) public onlyOwner {
StakingRewardsInfo storage info =
stakingRewardsInfoByStakingToken[stakingToken];
require(
info.stakingRewards == address(0),
"StakingRewardsFactory::deploy: already deployed"
);
info.stakingRewards = address(
new StakingRewards(
/*_rewardsDistribution=*/
address(this),
rewardTokens,
stakingToken
)
);
for (uint8 i = 0; i < rewardTokens.length; i++) {
require(
rewardAmounts[i] > 0,
"StakingRewardsFactory::addRewardToken: reward amount should be greater than 0"
);
info.poolRewardToken.push(rewardTokens[i]);
info.poolRewardAmount.push(rewardAmounts[i]);
}
stakingTokens.push(stakingToken);
}
// Rescue leftover funds from pool
function rescueFunds(address stakingToken, address tokenAddress)
public
onlyOwner
{
StakingRewardsInfo storage info =
stakingRewardsInfoByStakingToken[stakingToken];
require(
info.stakingRewards != address(0),
"StakingRewardsFactory::notifyRewardAmount: not deployed"
);
StakingRewards(info.stakingRewards).rescueFunds(
tokenAddress,
msg.sender
);
}
// Rescue leftover funds from factory
function rescueFactoryFunds(address tokenAddress) public onlyOwner {
IERC20 token = IERC20(tokenAddress);
uint256 balance = token.balanceOf(address(this));
require(balance > 0, "No balance for given token address");
token.transfer(msg.sender, balance);
}
///// permissionless functions
// call notifyRewardAmount for all staking tokens.
function notifyRewardAmounts() public {
require(
stakingTokens.length > 0,
"StakingRewardsFactory::notifyRewardAmounts: called before any deploys"
);
for (uint256 i = 0; i < stakingTokens.length; i++) {
notifyRewardAmount(stakingTokens[i]);
}
}
// notify reward amount for an individual staking token.
// this is a fallback in case the notifyRewardAmounts costs too much gas to call for all contracts
function notifyRewardAmount(address stakingToken) public {
require(
block.timestamp >= stakingRewardsGenesis,
"StakingRewardsFactory::notifyRewardAmount: not ready"
);
StakingRewardsInfo storage info =
stakingRewardsInfoByStakingToken[stakingToken];
require(
info.stakingRewards != address(0),
"StakingRewardsFactory::notifyRewardAmount: not deployed"
);
for (uint256 i = 0; i < info.poolRewardToken.length; i++) {
uint256 rewardAmount = info.poolRewardAmount[i];
if (rewardAmount > 0) {
info.poolRewardAmount[i] = 0;
require(
IERC20(info.poolRewardToken[i]).transfer(
info.stakingRewards,
rewardAmount
),
"StakingRewardsFactory::notifyRewardAmount: transfer failed"
);
StakingRewards(info.stakingRewards).notifyRewardAmount(
info.poolRewardToken[i],
rewardAmount
);
}
}
}
function stakingRewardsInfo(address stakingToken)
public
view
returns (
address,
address[] memory,
uint256[] memory
)
{
StakingRewardsInfo storage info =
stakingRewardsInfoByStakingToken[stakingToken];
return (
info.stakingRewards,
info.poolRewardToken,
info.poolRewardAmount
);
}
}