forked from Synthetixio/synthetix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRewardsDistribution.sol
251 lines (205 loc) · 7.84 KB
/
RewardsDistribution.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
/*
-----------------------------------------------------------------
FILE INFORMATION
-----------------------------------------------------------------
file: RewardsDistribution.sol
version: 1.0
author: Clinton Ennis, Jackson Chan
date: 2019-08-12
-----------------------------------------------------------------
MODULE DESCRIPTION
-----------------------------------------------------------------
Distributes the inflationary supply rewards after they have been
minted.
DistributionData can be added to the distributions array simply
with an address and an amount of tokens to send to that address.
i.e. The sETH arb pool is assigned 5% of the current Inflationary
supply so it is allocated 72K of the tokens. If that is the only
distribution added then 72K SNX is deducted from the weeks
inflationary supply and sent to the sETH Arb Pool then the
remainder is sent to the RewardsEscrow Contract for the SNX
Staking Rewards.
RewardDistributions can be added, edited and removed.
-----------------------------------------------------------------
*/
pragma solidity 0.4.25;
import "./Owned.sol";
import "./SafeDecimalMath.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IFeePool.sol";
contract RewardsDistribution is Owned {
using SafeMath for uint;
using SafeDecimalMath for uint;
/**
* @notice Authorised address able to call distributeRewards
*/
address public authority;
/**
* @notice Address of the Synthetix ProxyERC20
*/
address public synthetixProxy;
/**
* @notice Address of the RewardEscrow contract
*/
address public rewardEscrow;
/**
* @notice Address of the FeePoolProxy
*/
address public feePoolProxy;
/**
* @notice Stores an address and amount
* of the inflationary supply to sent to the address.
*/
struct DistributionData {
address destination;
uint amount;
}
/**
* @notice An array of addresses and amounts to send
*/
DistributionData[] public distributions;
/**
* @dev _authority maybe the underlying synthetix contract.
* Remember to set the autority on a synthetix upgrade
*/
constructor(address _owner, address _authority, address _synthetixProxy, address _rewardEscrow, address _feePoolProxy)
Owned(_owner)
public
{
authority = _authority;
synthetixProxy = _synthetixProxy;
rewardEscrow = _rewardEscrow;
feePoolProxy = _feePoolProxy;
}
// ========== EXTERNAL SETTERS ==========
function setSynthetixProxy(address _synthetixProxy)
external
onlyOwner
{
synthetixProxy = _synthetixProxy;
}
function setRewardEscrow(address _rewardEscrow)
external
onlyOwner
{
rewardEscrow = _rewardEscrow;
}
function setFeePoolProxy(address _feePoolProxy)
external
onlyOwner
{
feePoolProxy = _feePoolProxy;
}
/**
* @notice Set the address of the contract authorised to call distributeRewards()
* @param _authority Address of the authorised calling contract.
*/
function setAuthority(address _authority)
external
onlyOwner
{
authority = _authority;
}
// ========== EXTERNAL FUNCTIONS ==========
/**
* @notice Adds a Rewards DistributionData struct to the distributions
* array. Any entries here will be iterated and rewards distributed to
* each address when tokens are sent to this contract and distributeRewards()
* is called by the autority.
* @param destination An address to send rewards tokens too
* @param amount The amount of rewards tokens to send
*/
function addRewardDistribution(address destination, uint amount)
external
onlyOwner
returns (bool)
{
require(destination != address(0), "Cant add a zero address");
require(amount != 0, "Cant add a zero amount");
DistributionData memory rewardsDistribution = DistributionData(destination, amount);
distributions.push(rewardsDistribution);
emit RewardDistributionAdded(distributions.length - 1, destination, amount);
return true;
}
/**
* @notice Deletes a RewardDistribution from the distributions
* so it will no longer be included in the call to distributeRewards()
* @param index The index of the DistributionData to delete
*/
function removeRewardDistribution(uint index)
external
onlyOwner
{
require(index <= distributions.length - 1, "index out of bounds");
// shift distributions indexes across
for (uint i = index; i < distributions.length - 1; i++) {
distributions[i] = distributions[i+1];
}
distributions.length--;
// Since this function must shift all later entries down to fill the
// gap from the one it removed, it could in principle consume an
// unbounded amount of gas. However, the number of entries will
// presumably always be very low.
}
/**
* @notice Edits a RewardDistribution in the distributions array.
* @param index The index of the DistributionData to edit
* @param destination The destination address. Send the same address to keep or different address to change it.
* @param amount The amount of tokens to edit. Send the same number to keep or change the amount of tokens to send.
*/
function editRewardDistribution(uint index, address destination, uint amount)
external
onlyOwner
returns (bool)
{
require(index <= distributions.length - 1, "index out of bounds");
distributions[index].destination = destination;
distributions[index].amount = amount;
return true;
}
/**
* @notice Iterates the distributions sending set out amounts of
* tokens to the specified address. The remainder is then sent to the RewardEscrow Contract
* and applied to the FeePools staking rewards.
* @param amount The total number of tokens being distributed
*/
function distributeRewards(uint amount)
external
returns (bool)
{
require(msg.sender == authority, "Caller is not authorised");
require(rewardEscrow != address(0), "RewardEscrow is not set");
require(synthetixProxy != address(0), "SynthetixProxy is not set");
require(feePoolProxy != address(0), "FeePoolProxy is not set");
require(amount > 0, "Nothing to distribute");
require(IERC20(synthetixProxy).balanceOf(this) >= amount, "RewardsDistribution contract does not have enough tokens to distribute");
uint remainder = amount;
// Iterate the array of distributions sending the configured amounts
for (uint i = 0; i < distributions.length; i++) {
if (distributions[i].destination != address(0) || distributions[i].amount != 0) {
remainder = remainder.sub(distributions[i].amount);
IERC20(synthetixProxy).transfer(distributions[i].destination, distributions[i].amount);
}
}
// After all ditributions have been sent, send the remainder to the RewardsEscrow contract
IERC20(synthetixProxy).transfer(rewardEscrow, remainder);
// Tell the FeePool how much it has to distribute to the stakers
IFeePool(feePoolProxy).setRewardsToDistribute(remainder);
emit RewardsDistributed(amount);
return true;
}
/* ========== VIEWS ========== */
/**
* @notice Retrieve the length of the distributions array
*/
function distributionsLength()
external
view
returns (uint)
{
return distributions.length;
}
/* ========== Events ========== */
event RewardDistributionAdded(uint index, address destination, uint amount);
event RewardsDistributed(uint amount);
}