/
BaseMediatorFeeManager.sol
184 lines (162 loc) · 6.4 KB
/
BaseMediatorFeeManager.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
pragma solidity 0.4.24;
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* @title BaseMediatorFeeManager
* @dev Base fee manager to handle fees for AMB mediators.
*/
contract BaseMediatorFeeManager is Ownable {
using SafeMath for uint256;
event FeeUpdated(uint256 fee);
// This is not a real fee value but a relative value used to calculate the fee percentage.
// 1 ether = 100% of the value.
uint256 internal constant MAX_FEE = 1 ether;
uint256 internal constant MAX_REWARD_ACCOUNTS = 50;
uint256 public fee;
address[] internal rewardAccounts;
address internal mediatorContract;
modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}
/**
* @dev Stores the initial parameters of the fee manager.
* @param _owner address of the owner of the fee manager contract.
* @param _fee the fee percentage amount.
* @param _rewardAccountList list of addresses that will receive the fee rewards.
*/
constructor(address _owner, uint256 _fee, address[] _rewardAccountList, address _mediatorContract) public {
require(_rewardAccountList.length > 0 && _rewardAccountList.length <= MAX_REWARD_ACCOUNTS);
_transferOwnership(_owner);
_setFee(_fee);
mediatorContract = _mediatorContract;
for (uint256 i = 0; i < _rewardAccountList.length; i++) {
require(isValidAccount(_rewardAccountList[i]));
}
rewardAccounts = _rewardAccountList;
}
/**
* @dev Calculates the fee amount to be subtracted from the value.
* @param _value the base value from which fees are calculated
*/
function calculateFee(uint256 _value) external view returns (uint256) {
return _value.mul(fee).div(MAX_FEE);
}
/**
* @dev Stores the fee percentage amount for the mediator operations.
* @param _fee the fee percentage
*/
function _setFee(uint256 _fee) internal validFee(_fee) {
fee = _fee;
emit FeeUpdated(_fee);
}
/**
* @dev Sets the fee percentage amount for the mediator operations. Only the owner can call this method.
* @param _fee the fee percentage
*/
function setFee(uint256 _fee) external onlyOwner {
_setFee(_fee);
}
function isValidAccount(address _account) internal returns (bool) {
return _account != address(0) && _account != mediatorContract;
}
/**
* @dev Adds a new account to the list of accounts to receive rewards for the operations.
* Only the owner can call this method.
* @param _account new reward account
*/
function addRewardAccount(address _account) external onlyOwner {
require(isValidAccount(_account));
require(!isRewardAccount(_account));
require(rewardAccounts.length.add(1) < MAX_REWARD_ACCOUNTS);
rewardAccounts.push(_account);
}
/**
* @dev Removes an account from the list of accounts to receive rewards for the operations.
* Only the owner can call this method.
* finds the element, swaps it with the last element, and then deletes it;
* @param _account to be removed
* return boolean whether the element was found and deleted
*/
function removeRewardAccount(address _account) external onlyOwner returns (bool) {
uint256 numOfAccounts = rewardAccountsCount();
for (uint256 i = 0; i < numOfAccounts; i++) {
if (rewardAccounts[i] == _account) {
rewardAccounts[i] = rewardAccounts[numOfAccounts - 1];
delete rewardAccounts[numOfAccounts - 1];
rewardAccounts.length--;
return true;
}
}
// If account is not found and removed, the transactions is reverted
revert();
}
/**
* @dev Tells the amount of accounts in the list of reward accounts.
* @return amount of accounts.
*/
function rewardAccountsCount() public view returns (uint256) {
return rewardAccounts.length;
}
/**
* @dev Tells if the account is part of the list of reward accounts.
* @param _account to check if is part of the list.
* @return true if the account is in the list
*/
function isRewardAccount(address _account) internal view returns (bool) {
for (uint256 i = 0; i < rewardAccountsCount(); i++) {
if (rewardAccounts[i] == _account) {
return true;
}
}
return false;
}
/**
* @dev Tells the list of accounts that receives rewards for the operations.
* @return the list of reward accounts
*/
function rewardAccountsList() public view returns (address[]) {
return rewardAccounts;
}
/**
* @dev ERC677 transfer callback function, received fee is distributed.
* @param _value amount of transferred tokens
*/
function onTokenTransfer(address, uint256 _value, bytes) external returns (bool) {
distributeFee(_value);
return true;
}
/**
* @dev Distributes the provided amount of fees proportionally to the list of reward accounts.
* In case the fees cannot be equally distributed, the remaining difference will be distributed to an account
* in a semi-random way.
* @param _fee total amount to be distributed to the list of reward accounts.
*/
function distributeFee(uint256 _fee) internal {
uint256 numOfAccounts = rewardAccountsCount();
uint256 feePerAccount = _fee.div(numOfAccounts);
uint256 randomAccountIndex;
uint256 diff = _fee.sub(feePerAccount.mul(numOfAccounts));
if (diff > 0) {
randomAccountIndex = random(numOfAccounts);
}
for (uint256 i = 0; i < numOfAccounts; i++) {
uint256 feeToDistribute = feePerAccount;
if (diff > 0 && randomAccountIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
onFeeDistribution(rewardAccounts[i], feeToDistribute);
}
}
/**
* @dev Calculates a random number based on the block number.
* @param _count the max value for the random number.
* @return a number between 0 and _count.
*/
function random(uint256 _count) internal view returns (uint256) {
return uint256(blockhash(block.number.sub(1))) % _count;
}
/* solcov ignore next */
function onFeeDistribution(address _rewardAddress, uint256 _fee) internal;
}