-
Notifications
You must be signed in to change notification settings - Fork 608
/
Copy pathRewardsDistribution.sol
204 lines (165 loc) · 7.11 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
pragma solidity ^0.5.16;
// Inheritance
import "./Owned.sol";
import "./interfaces/IRewardsDistribution.sol";
// Libraires
import "./SafeDecimalMath.sol";
// Internal references
import "./interfaces/IERC20.sol";
import "./interfaces/IFeePool.sol";
import "./interfaces/IRewardsDistribution.sol";
// https://docs.synthetix.io/contracts/source/contracts/rewardsdistribution
contract RewardsDistribution is Owned, IRewardsDistribution {
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 An array of addresses and amounts to send
*/
DistributionData[] public distributions;
/**
* @dev _authority maybe the underlying synthetix contract.
* Remember to set the authority on a synthetix upgrade
*/
constructor(
address _owner,
address _authority,
address _synthetixProxy,
address _rewardEscrow,
address _feePoolProxy
) public Owned(_owner) {
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;
}
function distributeRewards(uint amount) external returns (bool) {
require(amount > 0, "Nothing to distribute");
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(
IERC20(synthetixProxy).balanceOf(address(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);
// Transfer the SNX
IERC20(synthetixProxy).transfer(distributions[i].destination, distributions[i].amount);
// If the contract implements RewardsDistributionRecipient.sol, inform it how many SNX its received.
bytes memory payload = abi.encodeWithSignature("notifyRewardAmount(uint256)", distributions[i].amount);
// solhint-disable avoid-low-level-calls
(bool success, bytes memory result) = distributions[i].destination.call(payload);
if (!success) {
// if the error was emitted by the destination contract, bubble
uint len = result.length;
assembly {
revert(add(result, 0x20), len)
}
}
}
}
// 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);
}