/
RewardsDistribution.sol
237 lines (189 loc) · 8.33 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
/*
-----------------------------------------------------------------
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 oETH 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 OKS is deducted from the weeks
inflationary supply and sent to the oETH Arb Pool then the
remainder is sent to the RewardsEscrow Contract for the OKS
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";
import "./interfaces/IOikos.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 Oikos ProxyERC20
*/
address public oikosProxy;
/**
* @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 oikos contract.
* Remember to set the autority on a oikos upgrade
*/
constructor(address _owner, address _authority, address _oikosProxy, address _rewardEscrow, address _feePoolProxy)
public
Owned(_owner)
{
authority = _authority;
oikosProxy = _oikosProxy;
rewardEscrow = _rewardEscrow;
feePoolProxy = _feePoolProxy;
}
// ========== EXTERNAL SETTERS ==========
function setOikosProxy(address _oikosProxy) external onlyOwner {
oikosProxy = _oikosProxy;
}
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 min(uint a, uint b) private pure returns (uint) {
return a < b ? a : b;
}
/**
* @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(oikosProxy != address(0), "OikosProxy is not set");
require(feePoolProxy != address(0), "FeePoolProxy is not set");
require(amount > 0, "Nothing to distribute");
require(
IERC20(oikosProxy).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 distribution amount is larger than remainder, we just send all remainder
uint distributionAmount = min(distributions[i].amount, remainder);
if (distributions[i].destination != address(0) || distributionAmount != 0) {
remainder = remainder.sub(distributionAmount);
// Transfer the OKS
IERC20(oikosProxy).transfer(distributions[i].destination, distributionAmount);
// If the contract implements RewardsDistributionRecipient.sol, inform it how many OKS its received.
bytes memory payload = abi.encodeWithSignature("notifyRewardAmount(uint256)", distributionAmount);
distributions[i].destination.call(payload);
// Note: we're ignoring the return value as it will fail for contracts that do not implement RewardsDistributionRecipient.sol
}
}
// After all ditributions have been sent, send the remainder to the RewardsEscrow contract
IERC20(oikosProxy).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);
}