Created
March 27, 2020 19:33
-
-
Save patitonar/fdcff47b13d7936997c95c8a34e30651 to your computer and use it in GitHub Desktop.
BaseMediatorFeeManager with linkedList
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
address internal constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF; | |
uint256 public fee; | |
mapping(address => address) internal accountsPointers; | |
uint256 public rewardAccountsCount; | |
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) public { | |
require(_rewardAccountList.length > 0 && _rewardAccountList.length <= MAX_REWARD_ACCOUNTS); | |
_transferOwnership(_owner); | |
_setFee(_fee); | |
rewardAccountsCount = _rewardAccountList.length; | |
for (uint256 i = 0; i < _rewardAccountList.length; i++) { | |
if (i == 0) { | |
accountsPointers[F_ADDR] = _rewardAccountList[i]; | |
if (_rewardAccountList.length == 1) { | |
accountsPointers[_rewardAccountList[i]] = F_ADDR; | |
} | |
} else if (i == _rewardAccountList.length - 1) { | |
accountsPointers[_rewardAccountList[i - 1]] = _rewardAccountList[i]; | |
accountsPointers[_rewardAccountList[i]] = F_ADDR; | |
} else { | |
accountsPointers[_rewardAccountList[i - 1]] = _rewardAccountList[i]; | |
} | |
} | |
} | |
/** | |
* @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); | |
} | |
/** | |
* @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(_account != address(0)); | |
require(!isRewardAccount(_account)); | |
require(rewardAccountsCount < MAX_REWARD_ACCOUNTS); | |
address firstAccount = accountsPointers[F_ADDR]; | |
require(firstAccount != address(0)); | |
accountsPointers[F_ADDR] = _account; | |
accountsPointers[_account] = firstAccount; | |
rewardAccountsCount = rewardAccountsCount.add(1); | |
} | |
/** | |
* @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) { | |
address nextAccount = accountsPointers[_account]; | |
address index = F_ADDR; | |
address next = accountsPointers[index]; | |
require(next != address(0)); | |
while (next != _account) { | |
index = next; | |
next = accountsPointers[index]; | |
require(next != F_ADDR && next != address(0)); | |
} | |
accountsPointers[index] = nextAccount; | |
delete accountsPointers[_account]; | |
rewardAccountsCount = rewardAccountsCount.sub(1); | |
return true; | |
} | |
/** | |
* @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) { | |
return _account != F_ADDR && accountsPointers[_account] != address(0); | |
} | |
/** | |
* @dev Tells the list of accounts that receives rewards for the operations. | |
* @return the list of reward accounts | |
*/ | |
function rewardAccountsList() public view returns (address[]) { | |
address[] memory list = new address[](rewardAccountsCount); | |
uint256 counter = 0; | |
address nextAccount = accountsPointers[F_ADDR]; | |
require(nextAccount != address(0)); | |
while (nextAccount != F_ADDR) { | |
list[counter] = nextAccount; | |
nextAccount = accountsPointers[nextAccount]; | |
counter++; | |
require(nextAccount != address(0)); | |
} | |
return list; | |
} | |
/** | |
* @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) public { | |
uint256 feePerAccount = _fee.div(rewardAccountsCount); | |
uint256 randomAccountIndex; | |
uint256 diff = _fee.sub(feePerAccount.mul(rewardAccountsCount)); | |
if (diff > 0) { | |
randomAccountIndex = random(rewardAccountsCount); | |
} | |
address nextAccount = accountsPointers[F_ADDR]; | |
require(nextAccount != address(0)); | |
uint256 i = 0; | |
while (nextAccount != F_ADDR) { | |
uint256 feeToDistribute = feePerAccount; | |
if (diff > 0 && randomAccountIndex == i) { | |
feeToDistribute = feeToDistribute.add(diff); | |
} | |
onFeeDistribution(nextAccount, feeToDistribute); | |
nextAccount = accountsPointers[nextAccount]; | |
require(nextAccount != address(0)); | |
i = i + 1; | |
} | |
} | |
/** | |
* @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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment