Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native to erc20 mediator on top of AMB #367

Merged
merged 35 commits into from
May 16, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8d8dba4
Extract basic amb mediators functionality
patitonar Jan 31, 2020
6bfb232
Add amb-native-to-erc mediators
patitonar Feb 4, 2020
a1bbc5f
Add amb-native-to-erc fee manager
patitonar Feb 7, 2020
0ef329e
Add executeActionOnFixedTokens methods
patitonar Feb 11, 2020
6ef0ae5
Add HomeAMBNativeToErc20 unit tests
patitonar Feb 11, 2020
f5d9027
Add rewardable HomeAMBNativeToErc20 unit tests
patitonar Feb 12, 2020
339f82c
Add basic ForeignAMBNativeToErc20 unit tests
patitonar Feb 12, 2020
466810a
Add main ForeignAMBNativeToErc20 unit tests
patitonar Feb 13, 2020
da90634
Update flatten script
patitonar Feb 13, 2020
cec5150
Fix verifier
patitonar Feb 14, 2020
b56d95f
Add amb-native-to-erc deploy scripts
patitonar Feb 14, 2020
a3d56a3
lint fixes
patitonar Feb 14, 2020
eff5df1
Add amb-native-to-erc docs
patitonar Feb 17, 2020
8cc1e65
Add ClassicHomeAMBNativeToErc20 to be used with classic proxy
patitonar Feb 18, 2020
4a615de
Store mediator nonce in uintStorage
patitonar Feb 21, 2020
529d193
Revert "Add ClassicHomeAMBNativeToErc20 to be used with classic proxy"
patitonar Feb 26, 2020
88b992c
Use fee manager as external contract
patitonar Mar 19, 2020
414c782
Merge branch 'develop' into 365-amb-native-to-erc
patitonar Mar 19, 2020
661b781
Extract safe native transfer functionality into library
patitonar Mar 19, 2020
0c68dcf
linter fixes
patitonar Mar 19, 2020
5fbc11e
lint fixes
patitonar Mar 19, 2020
0da48b3
add safeSendValue unit tests
patitonar Mar 20, 2020
652982f
Merge branch 'develop' into 365-amb-native-to-erc
patitonar Mar 30, 2020
aa0cd59
Avoid mediator contract as reward receiver
patitonar Mar 30, 2020
a183203
Block multiple requests to mediator on fee distribution
patitonar Apr 1, 2020
53ecb56
fix comment
patitonar Apr 1, 2020
d8b8379
simplify MediatorMessagesGuard
patitonar Apr 2, 2020
0c7afe8
fix modifier
patitonar Apr 2, 2020
0dfe77d
Add method to fix mediator balance
patitonar Apr 2, 2020
9efb764
Merge branch 'develop' into 365-amb-native-to-erc
patitonar Apr 15, 2020
9af110e
Lint fixes
patitonar Apr 15, 2020
6e611e6
Add TokensBridged event
patitonar Apr 16, 2020
69a612e
Update messages control implementation
patitonar Apr 17, 2020
8b1172b
Merge branch 'develop' into 365-amb-native-to-erc
patitonar Apr 21, 2020
3c7a0fc
Fix alternative receiver behaviour for failed messages
patitonar Apr 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/interfaces/IMediatorFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma solidity 0.4.24;

interface IMediatorFeeManager {
function calculateFee(uint256) external view returns (uint256);
}
203 changes: 70 additions & 133 deletions contracts/upgradeable_contracts/BaseMediatorFeeManager.sol
Original file line number Diff line number Diff line change
@@ -1,164 +1,108 @@
pragma solidity 0.4.24;

import "../upgradeability/EternalStorage.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";

/**
* @title BaseMediatorFeeManager
* @dev Common functionality of fee managers for AMB mediators to store, calculate and perform actions related to
* fee distribution. The fee manager is used as a logic contract only, the state variables are stored in the mediator
* contract, so methods should be invoked by using delegatecall or callcode.
* @dev Base fee manager to handle fees for AMB mediators.
*/
contract BaseMediatorFeeManager is EternalStorage {
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;
bytes32 internal constant FEE_STORAGE_KEY = 0x833b9f6abf0b529613680afe2a00fa663cc95cbdc47d726d85a044462eabbf02; // keccak256(abi.encodePacked("fee"))
bytes32 internal constant REWARD_ACCOUNTS_COUNT = 0x7a0619a0d97f7e9c83d1a6c44be033aecca1245b900bb0e97a7dcaae387eb874; // keccak256(abi.encodePacked("rewardAccountCount"))
address internal constant F_ADDR = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
uint256 internal constant MAX_REWARD_ACCOUNTS = 50;

uint256 public fee;
address[] internal rewardAccounts;

modifier validFee(uint256 _fee) {
require(_fee < MAX_FEE);
/* solcov ignore next */
_;
}

/**
* @dev Initialize the list of accounts that receives rewards for the mediator operations.
* The list of accounts is stored as a Linked List where the list starts and ends at F_ADDR.
* Example: F_ADDR -> account1; account1 -> account2; account2 -> F_ADDR
* @param _accounts list of accounts
*/
function initializeRewardAccounts(address[] _accounts) public {
require(_accounts.length > 0);
for (uint256 i = 0; i < _accounts.length; i++) {
require(_accounts[i] != address(0) && _accounts[i] != F_ADDR);
require(!isRewardAccount(_accounts[i]));

if (i == 0) {
setNextRewardAccount(F_ADDR, _accounts[i]);
if (_accounts.length == 1) {
setNextRewardAccount(_accounts[i], F_ADDR);
}
} else if (i == _accounts.length - 1) {
setNextRewardAccount(_accounts[i - 1], _accounts[i]);
setNextRewardAccount(_accounts[i], F_ADDR);
} else {
setNextRewardAccount(_accounts[i - 1], _accounts[i]);
}
}

setRewardAccountsCount(_accounts.length);
}

/**
* @dev Tells the list of accounts that receives rewards for the operations.
* @return the list of reward accounts
* @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.
*/
function rewardAccounts() external view returns (address[] memory) {
address[] memory list = new address[](rewardAccountsCount());
uint256 counter = 0;
address nextRewardAccount = getNextRewardAccount(F_ADDR);
require(nextRewardAccount != address(0));

while (nextRewardAccount != F_ADDR) {
list[counter] = nextRewardAccount;
nextRewardAccount = getNextRewardAccount(nextRewardAccount);
counter++;

require(nextRewardAccount != address(0));
}

return list;
constructor(address _owner, uint256 _fee, address[] _rewardAccountList) public {
require(_rewardAccountList.length > 0 && _rewardAccountList.length <= MAX_REWARD_ACCOUNTS);
_transferOwnership(_owner);
_setFee(_fee);
rewardAccounts = _rewardAccountList;
akolotov marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @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) {
uint256 fee = getFee();
return _value.mul(fee).div(MAX_FEE);
}

/**
* @dev Sets the fee percentage amount for the mediator operations.
* @dev Stores the fee percentage amount for the mediator operations.
* @param _fee the fee percentage
*/
function setFee(uint256 _fee) external validFee(_fee) {
uintStorage[FEE_STORAGE_KEY] = _fee;
function _setFee(uint256 _fee) internal validFee(_fee) {
fee = _fee;
emit FeeUpdated(_fee);
}

/**
* @dev Tells the fee percentage amount for the mediator operations.
* @return the fee percentage amount
* @dev Sets the fee percentage amount for the mediator operations. Only the owner can call this method.
* @param _fee the fee percentage
*/
function getFee() public view returns (uint256) {
return uintStorage[FEE_STORAGE_KEY];
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 {
require(_account != address(0) && _account != F_ADDR);
function addRewardAccount(address _account) external onlyOwner {
require(_account != address(0));
require(!isRewardAccount(_account));

address firstAccount = getNextRewardAccount(F_ADDR);
// if list wasn't initialized
if (firstAccount == address(0)) {
firstAccount = F_ADDR;
}
setNextRewardAccount(_account, firstAccount);
setNextRewardAccount(F_ADDR, _account);
setRewardAccountsCount(rewardAccountsCount().add(1));
require(rewardAccounts.length.add(1) < MAX_REWARD_ACCOUNTS);
rewardAccounts.push(_account);
akolotov marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @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 {
require(isRewardAccount(_account));
address accountNext = getNextRewardAccount(_account);
address index = F_ADDR;
address next = getNextRewardAccount(index);
require(next != address(0));

while (next != _account) {
index = next;
next = getNextRewardAccount(index);

require(next != F_ADDR && next != address(0));
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;
}
}

setNextRewardAccount(index, accountNext);
delete addressStorage[keccak256(abi.encodePacked("rewardAccountList", _account))];
setRewardAccountsCount(rewardAccountsCount().sub(1));
// 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() internal view returns (uint256) {
return uintStorage[REWARD_ACCOUNTS_COUNT];
}

/**
* @dev Stores the amount of accounts in the list of reward accounts.
* @param _count amount of accounts.
*/
function setRewardAccountsCount(uint256 _count) internal {
require(_count <= MAX_REWARD_ACCOUNTS);
uintStorage[REWARD_ACCOUNTS_COUNT] = _count;
function rewardAccountsCount() public view returns (uint256) {
return rewardAccounts.length;
}

/**
Expand All @@ -167,25 +111,29 @@ contract BaseMediatorFeeManager is EternalStorage {
* @return true if the account is in the list
*/
function isRewardAccount(address _account) internal view returns (bool) {
return _account != F_ADDR && getNextRewardAccount(_account) != address(0);
for (uint256 i = 0; i < rewardAccountsCount(); i++) {
if (rewardAccounts[i] == _account) {
return true;
}
}
return false;
}

/**
* @dev Tells the next account in the list of reward accounts.
* @param _address previous account in the list.
* @return _account next account in the list.
* @dev Tells the list of accounts that receives rewards for the operations.
* @return the list of reward accounts
*/
function getNextRewardAccount(address _address) internal view returns (address) {
return addressStorage[keccak256(abi.encodePacked("rewardAccountList", _address))];
function rewardAccountsList() public view returns (address[]) {
return rewardAccounts;
}

/**
* @dev Stores the next account in the list of reward accounts.
* @param _prevAccount previous account in the list.
* @param _account next account in the list.
* @dev ERC677 transfer callback function, received fee is distributed.
* @param _value amount of transferred tokens
*/
function setNextRewardAccount(address _prevAccount, address _account) internal {
addressStorage[keccak256(abi.encodePacked("rewardAccountList", _prevAccount))] = _account;
function onTokenTransfer(address, uint256 _value, bytes) external returns (bool) {
distributeFee(_value);
return true;
}

/**
Expand All @@ -194,32 +142,21 @@ contract BaseMediatorFeeManager is EternalStorage {
* in a semi-random way.
* @param _fee total amount to be distributed to the list of reward accounts.
*/
function distributeFee(uint256 _fee) external {
function distributeFee(uint256 _fee) internal {
uint256 numOfAccounts = rewardAccountsCount();
if (numOfAccounts > 0) {
uint256 feePerValidator = _fee.div(numOfAccounts);
uint256 randomValidatorIndex;
uint256 diff = _fee.sub(feePerValidator.mul(numOfAccounts));
if (diff > 0) {
randomValidatorIndex = random(numOfAccounts);
}

address nextRewardAccount = getNextRewardAccount(F_ADDR);
require((nextRewardAccount != F_ADDR) && (nextRewardAccount != address(0)));

uint256 i = 0;
while (nextRewardAccount != F_ADDR) {
uint256 feeToDistribute = feePerValidator;
if (diff > 0 && randomValidatorIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}

onFeeDistribution(nextRewardAccount, feeToDistribute);
uint256 feePerAccount = _fee.div(numOfAccounts);
uint256 randomAccountIndex;
uint256 diff = _fee.sub(feePerAccount.mul(numOfAccounts));
if (diff > 0) {
randomAccountIndex = random(numOfAccounts);
}

nextRewardAccount = getNextRewardAccount(nextRewardAccount);
require(nextRewardAccount != address(0));
i = i + 1;
for (uint256 i = 0; i < numOfAccounts; i++) {
uint256 feeToDistribute = feePerAccount;
if (diff > 0 && randomAccountIndex == i) {
feeToDistribute = feeToDistribute.add(diff);
}
onFeeDistribution(rewardAccounts[i], feeToDistribute);
}
}

Expand Down
Loading