Skip to content

Instantly share code, notes, and snippets.

@patitonar
Created March 27, 2020 19:33
Show Gist options
  • Save patitonar/fdcff47b13d7936997c95c8a34e30651 to your computer and use it in GitHub Desktop.
Save patitonar/fdcff47b13d7936997c95c8a34e30651 to your computer and use it in GitHub Desktop.
BaseMediatorFeeManager with linkedList
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