Move fee manager into home mediator (#462)
This commit is contained in:
parent
ab3529e776
commit
a1ff878b1f
|
@ -0,0 +1,23 @@
|
|||
pragma solidity 0.4.24;
|
||||
|
||||
import "../PermittableToken.sol";
|
||||
|
||||
contract PermittableTokenMock is PermittableToken {
|
||||
uint256 private _blockTimestamp;
|
||||
|
||||
constructor(string _name, string _symbol, uint8 _decimals, uint256 _chainId)
|
||||
public
|
||||
PermittableToken(_name, _symbol, _decimals, _chainId)
|
||||
{
|
||||
// solhint-disable-previous-line no-empty-blocks
|
||||
}
|
||||
|
||||
function setNow(uint256 _timestamp) public {
|
||||
_blockTimestamp = _timestamp;
|
||||
}
|
||||
|
||||
function _now() internal view returns (uint256) {
|
||||
return _blockTimestamp != 0 ? _blockTimestamp : now;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,6 +14,15 @@ contract BasicAMBMediator is Ownable {
|
|||
bytes32 internal constant MEDIATOR_CONTRACT = 0x98aa806e31e94a687a31c65769cb99670064dd7f5a87526da075c5fb4eab9880; // keccak256(abi.encodePacked("mediatorContract"))
|
||||
bytes32 internal constant REQUEST_GAS_LIMIT = 0x2dfd6c9f781bb6bbb5369c114e949b69ebb440ef3d4dd6b2836225eb1dc3a2be; // keccak256(abi.encodePacked("requestGasLimit"))
|
||||
|
||||
/**
|
||||
* @dev Throws if caller on the other side if not an associated mediator.
|
||||
*/
|
||||
modifier onlyMediator {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(messageSender() == mediatorContractOnOtherSide());
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the AMB bridge contract address. Only the owner can call this method.
|
||||
* @param _bridgeContract the address of the bridge contract.
|
||||
|
|
|
@ -40,9 +40,7 @@ contract TokenBridgeMediator is BasicAMBMediator, BasicTokenBridge, TransferInfo
|
|||
* @param _recipient address that will receive the tokens
|
||||
* @param _value amount of tokens to be received
|
||||
*/
|
||||
function handleBridgedTokens(address _recipient, uint256 _value) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(messageSender() == mediatorContractOnOtherSide());
|
||||
function handleBridgedTokens(address _recipient, uint256 _value) external onlyMediator {
|
||||
if (withinExecutionLimit(_value)) {
|
||||
addTotalExecutedPerDay(getCurrentDay(), _value);
|
||||
executeActionOnBridgedTokens(_recipient, _value);
|
||||
|
@ -71,9 +69,7 @@ contract TokenBridgeMediator is BasicAMBMediator, BasicTokenBridge, TransferInfo
|
|||
* It uses the information stored by passMessage method when the assets were initially transferred
|
||||
* @param _messageId id of the message which execution failed on the other network.
|
||||
*/
|
||||
function fixFailedMessage(bytes32 _messageId) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(messageSender() == mediatorContractOnOtherSide());
|
||||
function fixFailedMessage(bytes32 _messageId) external onlyMediator {
|
||||
require(!messageFixed(_messageId));
|
||||
|
||||
address recipient = messageRecipient(_messageId);
|
||||
|
|
|
@ -266,14 +266,18 @@ contract BasicMultiTokenBridge is EternalStorage, Ownable {
|
|||
uint256 _dailyLimit = dailyLimit(address(0)).div(factor);
|
||||
uint256 _executionMaxPerTx = executionMaxPerTx(address(0)).div(factor);
|
||||
uint256 _executionDailyLimit = executionDailyLimit(address(0)).div(factor);
|
||||
|
||||
// such situation can happen when calculated limits relative to the token decimals are too low
|
||||
// e.g. minPerTx(address(0)) == 10 ** 14, _decimals == 3. _minPerTx happens to be 0, which is not allowed.
|
||||
// in this case, limits are raised to the default values
|
||||
if (_minPerTx == 0) {
|
||||
_minPerTx = 1;
|
||||
if (_maxPerTx <= _minPerTx) {
|
||||
_maxPerTx = 2;
|
||||
_executionMaxPerTx = 2;
|
||||
_maxPerTx = 100;
|
||||
_executionMaxPerTx = 100;
|
||||
if (_dailyLimit <= _maxPerTx || _executionDailyLimit <= _executionMaxPerTx) {
|
||||
_dailyLimit = 3;
|
||||
_executionDailyLimit = 3;
|
||||
_dailyLimit = 10000;
|
||||
_executionDailyLimit = 10000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ pragma solidity 0.4.24;
|
|||
|
||||
import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol";
|
||||
import "./BasicMultiAMBErc20ToErc677.sol";
|
||||
import "./ForeignFeeManagerMultiAMBErc20ToErc677.sol";
|
||||
import "./HomeMultiAMBErc20ToErc677.sol";
|
||||
import "../../libraries/TokenReader.sol";
|
||||
|
||||
/**
|
||||
|
@ -10,9 +10,7 @@ import "../../libraries/TokenReader.sol";
|
|||
* @dev Foreign side implementation for multi-erc20-to-erc677 mediator intended to work on top of AMB bridge.
|
||||
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
|
||||
*/
|
||||
contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeManagerMultiAMBErc20ToErc677 {
|
||||
bytes4 internal constant DEPLOY_AND_HANDLE_BRIDGE_TOKENS = 0x2ae87cdd; // deployAndHandleBridgedTokens(address,string,string,uint8,address,uint256)
|
||||
|
||||
contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
||||
/**
|
||||
* @dev Stores the initial parameters of the mediator.
|
||||
* @param _bridgeContract the address of the AMB bridge contract.
|
||||
|
@ -23,9 +21,6 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
* [ 0 = executionDailyLimit, 1 = executionMaxPerTx ]
|
||||
* @param _requestGasLimit the gas limit for the message execution.
|
||||
* @param _owner address of the owner of the mediator contract.
|
||||
* @param _rewardAddreses list of reward addresses, between whom fees will be distributed.
|
||||
* @param _fees array with initial fees for both bridge firections.
|
||||
* [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
*/
|
||||
function initialize(
|
||||
address _bridgeContract,
|
||||
|
@ -33,9 +28,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
uint256[3] _dailyLimitMaxPerTxMinPerTxArray, // [ 0 = _dailyLimit, 1 = _maxPerTx, 2 = _minPerTx ]
|
||||
uint256[2] _executionDailyLimitExecutionMaxPerTxArray, // [ 0 = _executionDailyLimit, 1 = _executionMaxPerTx ]
|
||||
uint256 _requestGasLimit,
|
||||
address _owner,
|
||||
address[] _rewardAddreses,
|
||||
uint256[2] _fees // [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
address _owner
|
||||
) external onlyRelevantSender returns (bool) {
|
||||
require(!isInitialized());
|
||||
require(_owner != address(0));
|
||||
|
@ -46,11 +39,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
_setExecutionLimits(address(0), _executionDailyLimitExecutionMaxPerTxArray);
|
||||
_setRequestGasLimit(_requestGasLimit);
|
||||
setOwner(_owner);
|
||||
if (_rewardAddreses.length > 0) {
|
||||
_setRewardAddressList(_rewardAddreses);
|
||||
}
|
||||
_setFee(HOME_TO_FOREIGN_FEE, address(0), _fees[0]);
|
||||
_setFee(FOREIGN_TO_HOME_FEE, address(0), _fees[1]);
|
||||
|
||||
setInitialize();
|
||||
|
||||
return isInitialized();
|
||||
|
@ -64,15 +53,9 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
*/
|
||||
function executeActionOnBridgedTokens(address _token, address _recipient, uint256 _value) internal {
|
||||
bytes32 _messageId = messageId();
|
||||
uint256 valueToTransfer = _value;
|
||||
uint256 fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToTransfer);
|
||||
if (fee > 0) {
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
valueToTransfer = valueToTransfer.sub(fee);
|
||||
}
|
||||
ERC677(_token).transfer(_recipient, valueToTransfer);
|
||||
ERC677(_token).transfer(_recipient, _value);
|
||||
_setMediatorBalance(_token, mediatorBalance(_token).sub(_value));
|
||||
emit TokensBridged(_token, _recipient, valueToTransfer, _messageId);
|
||||
emit TokensBridged(_token, _recipient, _value, _messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,6 +72,18 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Handles the bridged tokens. Checks that the value is inside the execution limits and invokes the method
|
||||
* to execute the Mint or Unlock accordingly.
|
||||
* @param _token bridged ERC20 token.
|
||||
* @param _recipient address that will receive the tokens.
|
||||
* @param _value amount of tokens to be received.
|
||||
*/
|
||||
function handleBridgedTokens(ERC677 _token, address _recipient, uint256 _value) external onlyMediator {
|
||||
require(isTokenRegistered(_token));
|
||||
_handleBridgedTokens(_token, _recipient, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates that the token amount is inside the limits, calls transferFrom to transfer the tokens to the contract
|
||||
* and invokes the method to burn/lock the tokens and unlock/mint the tokens on the other network.
|
||||
|
@ -130,8 +125,6 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
require(bytes(name).length > 0 || bytes(symbol).length > 0);
|
||||
|
||||
_initializeTokenBridgeLimits(_token, decimals);
|
||||
_setFee(HOME_TO_FOREIGN_FEE, _token, getFee(HOME_TO_FOREIGN_FEE, address(0)));
|
||||
_setFee(FOREIGN_TO_HOME_FEE, _token, getFee(FOREIGN_TO_HOME_FEE, address(0)));
|
||||
}
|
||||
|
||||
require(withinLimit(_token, _value));
|
||||
|
@ -139,30 +132,22 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
|
||||
bytes memory data;
|
||||
address receiver = chooseReceiver(_from, _data);
|
||||
uint256 valueToBridge = _value;
|
||||
uint256 fee = _distributeFee(FOREIGN_TO_HOME_FEE, _token, valueToBridge);
|
||||
if (fee > 0) {
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
valueToBridge = valueToBridge.sub(fee);
|
||||
}
|
||||
|
||||
if (isKnownToken) {
|
||||
data = abi.encodeWithSelector(this.handleBridgedTokens.selector, _token, receiver, valueToBridge);
|
||||
data = abi.encodeWithSelector(this.handleBridgedTokens.selector, _token, receiver, _value);
|
||||
} else {
|
||||
data = abi.encodeWithSelector(
|
||||
DEPLOY_AND_HANDLE_BRIDGE_TOKENS,
|
||||
HomeMultiAMBErc20ToErc677(this).deployAndHandleBridgedTokens.selector,
|
||||
_token,
|
||||
name,
|
||||
symbol,
|
||||
decimals,
|
||||
receiver,
|
||||
valueToBridge
|
||||
_value
|
||||
);
|
||||
}
|
||||
|
||||
// avoid stack too deep error by using existing variable
|
||||
fee = mediatorBalance(_token).add(valueToBridge);
|
||||
_setMediatorBalance(_token, fee);
|
||||
_setMediatorBalance(_token, mediatorBalance(_token).add(_value));
|
||||
|
||||
bytes32 _messageId = bridgeContract().requireToPassMessage(
|
||||
mediatorContractOnOtherSide(),
|
||||
|
@ -171,7 +156,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, ForeignFeeM
|
|||
);
|
||||
|
||||
setMessageToken(_messageId, _token);
|
||||
setMessageValue(_messageId, valueToBridge);
|
||||
setMessageValue(_messageId, _value);
|
||||
setMessageRecipient(_messageId, _from);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,14 @@ import "./BasicMultiTokenBridge.sol";
|
|||
import "../BaseRewardAddressList.sol";
|
||||
import "../Ownable.sol";
|
||||
import "../../interfaces/ERC677.sol";
|
||||
import "../../interfaces/IBurnableMintableERC677Token.sol";
|
||||
|
||||
/**
|
||||
* @title ForeignFeeManagerMultiAMBErc20ToErc677
|
||||
* @title HomeFeeManagerMultiAMBErc20ToErc677
|
||||
* @dev Implements the logic to distribute fees from the multi erc20 to erc677 mediator contract operations.
|
||||
* The fees are distributed in the form of native tokens to the list of reward accounts.
|
||||
*/
|
||||
contract ForeignFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownable, BasicMultiTokenBridge {
|
||||
contract HomeFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownable, BasicMultiTokenBridge {
|
||||
using SafeMath for uint256;
|
||||
|
||||
event FeeUpdated(bytes32 feeType, address indexed token, uint256 fee);
|
||||
|
@ -141,7 +142,11 @@ contract ForeignFeeManagerMultiAMBErc20ToErc677 is BaseRewardAddressList, Ownabl
|
|||
feeToDistribute = feeToDistribute.add(diff);
|
||||
}
|
||||
|
||||
ERC677(_token).transfer(nextAddr, feeToDistribute);
|
||||
if (_feeType == HOME_TO_FOREIGN_FEE) {
|
||||
ERC677(_token).transfer(nextAddr, feeToDistribute);
|
||||
} else {
|
||||
IBurnableMintableERC677Token(_token).mint(nextAddr, feeToDistribute);
|
||||
}
|
||||
|
||||
nextAddr = getNextRewardAddress(nextAddr);
|
||||
require(nextAddr != address(0));
|
|
@ -2,6 +2,7 @@ pragma solidity 0.4.24;
|
|||
|
||||
import "./BasicMultiAMBErc20ToErc677.sol";
|
||||
import "./TokenProxy.sol";
|
||||
import "./HomeFeeManagerMultiAMBErc20ToErc677.sol";
|
||||
import "../../interfaces/IBurnableMintableERC677Token.sol";
|
||||
|
||||
/**
|
||||
|
@ -9,7 +10,7 @@ import "../../interfaces/IBurnableMintableERC677Token.sol";
|
|||
* @dev Home side implementation for multi-erc20-to-erc677 mediator intended to work on top of AMB bridge.
|
||||
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
|
||||
*/
|
||||
contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
||||
contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677, HomeFeeManagerMultiAMBErc20ToErc677 {
|
||||
bytes32 internal constant TOKEN_IMAGE_CONTRACT = 0x20b8ca26cc94f39fab299954184cf3a9bd04f69543e4f454fab299f015b8130f; // keccak256(abi.encodePacked("tokenImageContract"))
|
||||
|
||||
event NewTokenRegistered(address indexed foreignToken, address indexed homeToken);
|
||||
|
@ -25,6 +26,9 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
* @param _requestGasLimit the gas limit for the message execution.
|
||||
* @param _owner address of the owner of the mediator contract.
|
||||
* @param _tokenImage address of the PermittableToken contract that will be used for deploying of new tokens.
|
||||
* @param _rewardAddreses list of reward addresses, between whom fees will be distributed.
|
||||
* @param _fees array with initial fees for both bridge firections.
|
||||
* [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
*/
|
||||
function initialize(
|
||||
address _bridgeContract,
|
||||
|
@ -33,7 +37,9 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
uint256[2] _executionDailyLimitExecutionMaxPerTxArray, // [ 0 = _executionDailyLimit, 1 = _executionMaxPerTx ]
|
||||
uint256 _requestGasLimit,
|
||||
address _owner,
|
||||
address _tokenImage
|
||||
address _tokenImage,
|
||||
address[] _rewardAddreses,
|
||||
uint256[2] _fees // [ 0 = homeToForeignFee, 1 = foreignToHomeFee ]
|
||||
) external onlyRelevantSender returns (bool) {
|
||||
require(!isInitialized());
|
||||
require(_owner != address(0));
|
||||
|
@ -45,6 +51,12 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
_setRequestGasLimit(_requestGasLimit);
|
||||
setOwner(_owner);
|
||||
_setTokenImage(_tokenImage);
|
||||
if (_rewardAddreses.length > 0) {
|
||||
_setRewardAddressList(_rewardAddreses);
|
||||
}
|
||||
_setFee(HOME_TO_FOREIGN_FEE, address(0), _fees[0]);
|
||||
_setFee(FOREIGN_TO_HOME_FEE, address(0), _fees[1]);
|
||||
|
||||
setInitialize();
|
||||
|
||||
return isInitialized();
|
||||
|
@ -84,7 +96,7 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
uint8 _decimals,
|
||||
address _recipient,
|
||||
uint256 _value
|
||||
) external {
|
||||
) external onlyMediator {
|
||||
string memory name = _name;
|
||||
string memory symbol = _symbol;
|
||||
if (bytes(name).length == 0) {
|
||||
|
@ -96,7 +108,9 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
address homeToken = new TokenProxy(tokenImage(), name, symbol, _decimals, bridgeContract().sourceChainId());
|
||||
_setTokenAddressPair(_token, homeToken);
|
||||
_initializeTokenBridgeLimits(homeToken, _decimals);
|
||||
super.handleBridgedTokens(ERC677(homeToken), _recipient, _value);
|
||||
_setFee(HOME_TO_FOREIGN_FEE, homeToken, getFee(HOME_TO_FOREIGN_FEE, address(0)));
|
||||
_setFee(FOREIGN_TO_HOME_FEE, homeToken, getFee(FOREIGN_TO_HOME_FEE, address(0)));
|
||||
_handleBridgedTokens(ERC677(homeToken), _recipient, _value);
|
||||
|
||||
emit NewTokenRegistered(_token, homeToken);
|
||||
}
|
||||
|
@ -108,9 +122,10 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
* @param _recipient address that will receive the tokens.
|
||||
* @param _value amount of tokens to be received.
|
||||
*/
|
||||
function handleBridgedTokens(ERC677 _token, address _recipient, uint256 _value) public {
|
||||
function handleBridgedTokens(ERC677 _token, address _recipient, uint256 _value) external onlyMediator {
|
||||
ERC677 homeToken = ERC677(homeTokenAddress(_token));
|
||||
super.handleBridgedTokens(homeToken, _recipient, _value);
|
||||
require(isTokenRegistered(homeToken));
|
||||
_handleBridgedTokens(homeToken, _recipient, _value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,6 +138,9 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
// if onTokenTransfer is called as a part of call to _relayTokens, this callback does nothing
|
||||
if (!lock()) {
|
||||
ERC677 token = ERC677(msg.sender);
|
||||
// if msg.sender if not a valid token contract, this check will fail, since limits are zeros
|
||||
// so the following check is not needed
|
||||
// require(isTokenRegistered(token));
|
||||
require(withinLimit(token, _value));
|
||||
addTotalSpentPerDay(token, getCurrentDay(), _value);
|
||||
bridgeSpecificActionsOnTokenTransfer(token, _from, _value, _data);
|
||||
|
@ -145,6 +163,9 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
// which will call passMessage.
|
||||
require(!lock());
|
||||
address to = address(this);
|
||||
// if msg.sender if not a valid token contract, this check will fail, since limits are zeros
|
||||
// so the following check is not needed
|
||||
// require(isTokenRegistered(token));
|
||||
require(withinLimit(token, _value));
|
||||
addTotalSpentPerDay(token, getCurrentDay(), _value);
|
||||
|
||||
|
@ -161,8 +182,14 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
*/
|
||||
function executeActionOnBridgedTokens(address _token, address _recipient, uint256 _value) internal {
|
||||
bytes32 _messageId = messageId();
|
||||
IBurnableMintableERC677Token(_token).mint(_recipient, _value);
|
||||
emit TokensBridged(_token, _recipient, _value, _messageId);
|
||||
uint256 valueToMint = _value;
|
||||
uint256 fee = _distributeFee(FOREIGN_TO_HOME_FEE, _token, valueToMint);
|
||||
if (fee > 0) {
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
valueToMint = valueToMint.sub(fee);
|
||||
}
|
||||
IBurnableMintableERC677Token(_token).mint(_recipient, valueToMint);
|
||||
emit TokensBridged(_token, _recipient, valueToMint, _messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,8 +248,15 @@ contract HomeMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 {
|
|||
*/
|
||||
function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal {
|
||||
if (!lock()) {
|
||||
IBurnableMintableERC677Token(_token).burn(_value);
|
||||
passMessage(_token, _from, chooseReceiver(_from, _data), _value);
|
||||
bytes32 _messageId = messageId();
|
||||
uint256 valueToBridge = _value;
|
||||
uint256 fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToBridge);
|
||||
if (fee > 0) {
|
||||
emit FeeDistributed(fee, _token, _messageId);
|
||||
valueToBridge = valueToBridge.sub(fee);
|
||||
}
|
||||
IBurnableMintableERC677Token(_token).burn(valueToBridge);
|
||||
passMessage(_token, _from, chooseReceiver(_from, _data), valueToBridge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,9 +43,7 @@ contract MultiTokenBridgeMediator is
|
|||
* @param _recipient address that will receive the tokens
|
||||
* @param _value amount of tokens to be received
|
||||
*/
|
||||
function handleBridgedTokens(ERC677 _token, address _recipient, uint256 _value) public {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(messageSender() == mediatorContractOnOtherSide());
|
||||
function _handleBridgedTokens(ERC677 _token, address _recipient, uint256 _value) internal {
|
||||
if (withinExecutionLimit(_token, _value)) {
|
||||
addTotalExecutedPerDay(_token, getCurrentDay(), _value);
|
||||
executeActionOnBridgedTokens(_token, _recipient, _value);
|
||||
|
@ -74,9 +72,7 @@ contract MultiTokenBridgeMediator is
|
|||
* It uses the information stored by passMessage method when the assets were initially transferred
|
||||
* @param _messageId id of the message which execution failed on the other network.
|
||||
*/
|
||||
function fixFailedMessage(bytes32 _messageId) external {
|
||||
require(msg.sender == address(bridgeContract()));
|
||||
require(messageSender() == mediatorContractOnOtherSide());
|
||||
function fixFailedMessage(bytes32 _messageId) external onlyMediator {
|
||||
require(!messageFixed(_messageId));
|
||||
|
||||
address token = messageToken(_messageId);
|
||||
|
|
|
@ -72,7 +72,7 @@ BasicHomeAMB::executeAffirmation
|
|||
..........HomeMultiAMBErc20ToErc677::_setTokenAddressPair
|
||||
..........BasicMultiTokenBridge::_initializeTokenBridgeLimits
|
||||
............emit NewTokenRegistered
|
||||
..........MultiTokenBridgeMediator::handleBridgedTokens
|
||||
..........MultiTokenBridgeMediator::_handleBridgedTokens
|
||||
............HomeMultiAMBErc20ToErc677::executeActionOnBridgedTokens
|
||||
..............ERC677BridgeToken::mint
|
||||
................<######>
|
||||
|
@ -96,7 +96,7 @@ BasicHomeAMB::executeAffirmation
|
|||
>>Mediator
|
||||
........HomeMultiAMBErc20ToErc677::handleBridgedTokens
|
||||
..........HomeMultiAMBErc20ToErc677::homeTokenAddress
|
||||
..........MultiTokenBridgeMediator::handleBridgedTokens
|
||||
..........MultiTokenBridgeMediator::_handleBridgedTokens
|
||||
............HomeMultiAMBErc20ToErc677::executeActionOnBridgedTokens
|
||||
..............ERC677BridgeToken::mint
|
||||
................<######>
|
||||
|
@ -156,14 +156,15 @@ BasicForeignAMB::executeSignatures
|
|||
........MessageProcessor::setMessageSender
|
||||
........MessageProcessor::setMessageId
|
||||
>>Mediator
|
||||
........MultiTokenBridgeMediator::handleBridgedTokens
|
||||
..........ForeignMultiAMBErc20ToErc677::executeActionOnBridgedTokens
|
||||
............ForeignFeeManagerMultiAMBErc20ToErc677::_distributeFee
|
||||
............ForeignMultiAMBErc20ToErc677::_setMediatorBalance
|
||||
........ForeignMultiAMBErc20ToErc677::handleBridgedTokens
|
||||
..........MultiTokenBridgeMediator::_handleBridgedTokens
|
||||
............ForeignMultiAMBErc20ToErc677::executeActionOnBridgedTokens
|
||||
..............ForeignFeeManagerMultiAMBErc20ToErc677::_distributeFee
|
||||
..............ForeignMultiAMBErc20ToErc677::_setMediatorBalance
|
||||
>>Token
|
||||
..............ERC20::transfer
|
||||
................ERC20::transfer
|
||||
>>Mediator
|
||||
............emit TokensBridged
|
||||
..............emit TokensBridged
|
||||
>>Bridge
|
||||
......MessageProcessor::setMessageCallStatus
|
||||
......ForeignAMB::emitEventOnMessageProcessed
|
||||
|
@ -199,7 +200,7 @@ BasicForeignAMB::executeSignatures
|
|||
........MessageProcessor::setMessageSender
|
||||
........MessageProcessor::setMessageId
|
||||
>>Mediator
|
||||
........[failed MultiTokenBridgeMediator::handleBridgedTokens]
|
||||
........[failed ForeignMultiAMBErc20ToErc677::handleBridgedTokens]
|
||||
>>Bridge
|
||||
......MessageProcessor::setMessageCallStatus
|
||||
......MessageProcessor::setFailedMessageReceiver
|
||||
|
@ -269,7 +270,7 @@ BasicHomeAMB::executeAffirmation
|
|||
........MessageProcessor::setMessageSender
|
||||
........MessageProcessor::setMessageId
|
||||
>>Mediator
|
||||
........[failed MultiTokenBridgeMediator::handleBridgedTokens/deployAndHandleBridgedTokens]
|
||||
........[failed HomeMultiAMBErc20ToErc677::handleBridgedTokens/deployAndHandleBridgedTokens]
|
||||
>>Bridge
|
||||
......MessageProcessor::setMessageCallStatus
|
||||
......MessageProcessor::setFailedMessageReceiver
|
||||
|
|
|
@ -1053,28 +1053,28 @@ HOME_MEDIATOR_REQUEST_GAS_LIMIT=2000000
|
|||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=2000000
|
||||
|
||||
# Variable to define whether to collect fee on bridge transfers
|
||||
# On this this bridge mode, fees collection on home side is not supported, should be false.
|
||||
HOME_REWARDABLE=false
|
||||
# On this bridge mode only BOTH_DIRECTIONS is supported, leave false to disable fees collection
|
||||
HOME_REWARDABLE=false
|
||||
# On this this bridge mode, fees collection on home side is not supported, should be false.
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
# Fee to be taken for every transaction directed from the Home network to the Foreign network
|
||||
# Makes sense only when FOREIGN_REWARDABLE=BOTH_DIRECTIONS
|
||||
# Makes sense only when HOME_REWARDABLE=BOTH_DIRECTIONS
|
||||
# e.g. 0.1% fee
|
||||
HOME_TRANSACTIONS_FEE=0.001
|
||||
# Fee to be taken for every transaction directed from the Foreign network to the Home network
|
||||
# Makes sense only when FOREIGN_REWARDABLE=BOTH_DIRECTIONS
|
||||
# Makes sense only when HOME_REWARDABLE=BOTH_DIRECTIONS
|
||||
# e.g. 0.1% fee
|
||||
FOREIGN_TRANSACTIONS_FEE=0.001
|
||||
|
||||
# List of accounts where rewards should be transferred in Home network separated by space without quotes
|
||||
# Makes sense only when FOREIGN_REWARDABLE=BOTH_DIRECTIONS
|
||||
#E.g. FOREIGN_MEDIATOR_REWARD_ACCOUNTS=0x 0x 0x
|
||||
FOREIGN_MEDIATOR_REWARD_ACCOUNTS=0x
|
||||
# Makes sense only when HOME_REWARDABLE=BOTH_DIRECTIONS
|
||||
#E.g. HOME_MEDIATOR_REWARD_ACCOUNTS=0x 0x 0x
|
||||
HOME_MEDIATOR_REWARD_ACCOUNTS=0x
|
||||
|
||||
# address of an already deployed PermittableToken contract that will be used as an implementation for all new created tokens
|
||||
# leave 0x, if you want to deploy a new PermittableToken for further usage
|
||||
HOME_ERC677_TOKEN_IMAGE=0x
|
||||
# leave empty, if you want to deploy a new PermittableToken for further usage
|
||||
HOME_ERC677_TOKEN_IMAGE=
|
||||
|
||||
# The api url of an explorer to verify all the deployed contracts in Home network. Supported explorers: Blockscout, etherscan
|
||||
#HOME_EXPLORER_URL=https://blockscout.com/poa/core/api
|
||||
|
|
|
@ -408,20 +408,20 @@ if (env.BRIDGE_MODE === 'AMB_ERC_TO_NATIVE') {
|
|||
}
|
||||
|
||||
if (env.BRIDGE_MODE === 'MULTI_AMB_ERC_TO_ERC') {
|
||||
if (FOREIGN_REWARDABLE === 'ONE_DIRECTION') {
|
||||
if (HOME_REWARDABLE === 'ONE_DIRECTION') {
|
||||
throw new Error(
|
||||
`Only BOTH_DIRECTIONS is supported for collecting fees on Foreign Network on ${BRIDGE_MODE} bridge mode.`
|
||||
`Only BOTH_DIRECTIONS is supported for collecting fees on Home Network on ${BRIDGE_MODE} bridge mode.`
|
||||
)
|
||||
}
|
||||
|
||||
if (HOME_REWARDABLE !== 'false') {
|
||||
throw new Error(`Collecting fees on Home Network on ${BRIDGE_MODE} bridge mode is not supported.`)
|
||||
if (FOREIGN_REWARDABLE !== 'false') {
|
||||
throw new Error(`Collecting fees on Foreign Network on ${BRIDGE_MODE} bridge mode is not supported.`)
|
||||
}
|
||||
|
||||
if (FOREIGN_REWARDABLE === 'BOTH_DIRECTIONS') {
|
||||
if (HOME_REWARDABLE === 'BOTH_DIRECTIONS') {
|
||||
validations = {
|
||||
...validations,
|
||||
FOREIGN_MEDIATOR_REWARD_ACCOUNTS: addressesValidator()
|
||||
HOME_MEDIATOR_REWARD_ACCOUNTS: addressesValidator()
|
||||
}
|
||||
}
|
||||
validations = {
|
||||
|
|
|
@ -21,11 +21,7 @@ const {
|
|||
FOREIGN_UPGRADEABLE_ADMIN,
|
||||
FOREIGN_AMB_BRIDGE,
|
||||
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT,
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY,
|
||||
FOREIGN_REWARDABLE,
|
||||
HOME_TRANSACTIONS_FEE,
|
||||
FOREIGN_TRANSACTIONS_FEE,
|
||||
FOREIGN_MEDIATOR_REWARD_ACCOUNTS
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY
|
||||
} = require('../loadEnv')
|
||||
|
||||
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
|
||||
|
@ -41,10 +37,7 @@ async function initializeMediator({
|
|||
executionDailyLimit,
|
||||
executionMaxPerTx,
|
||||
requestGasLimit,
|
||||
owner,
|
||||
rewardAddressList,
|
||||
homeToForeignFee,
|
||||
foreignToHomeFee
|
||||
owner
|
||||
}
|
||||
}) {
|
||||
console.log(`
|
||||
|
@ -56,14 +49,7 @@ async function initializeMediator({
|
|||
EXECUTION_DAILY_LIMIT : ${executionDailyLimit} which is ${Web3Utils.fromWei(executionDailyLimit)} in eth,
|
||||
EXECUTION_MAX_AMOUNT_PER_TX: ${executionMaxPerTx} which is ${Web3Utils.fromWei(executionMaxPerTx)} in eth,
|
||||
MEDIATOR_REQUEST_GAS_LIMIT : ${requestGasLimit},
|
||||
OWNER: ${owner},
|
||||
REWARD_ADDRESS_LIST: [${rewardAddressList.join(', ')}]`)
|
||||
if (FOREIGN_REWARDABLE) {
|
||||
console.log(`
|
||||
HOME_TO_FOREIGN_FEE: ${homeToForeignFee} which is ${HOME_TRANSACTIONS_FEE * 100}%
|
||||
FOREIGN_TO_HOME_FEE: ${foreignToHomeFee} which is ${FOREIGN_TRANSACTIONS_FEE * 100}%
|
||||
`)
|
||||
}
|
||||
OWNER: ${owner}`)
|
||||
|
||||
return contract.methods
|
||||
.initialize(
|
||||
|
@ -72,9 +58,7 @@ async function initializeMediator({
|
|||
[dailyLimit.toString(), maxPerTx.toString(), minPerTx.toString()],
|
||||
[executionDailyLimit.toString(), executionMaxPerTx.toString()],
|
||||
requestGasLimit,
|
||||
owner,
|
||||
rewardAddressList,
|
||||
[homeToForeignFee.toString(), foreignToHomeFee.toString()]
|
||||
owner
|
||||
)
|
||||
.encodeABI()
|
||||
}
|
||||
|
@ -84,13 +68,6 @@ async function initialize({ homeBridge, foreignBridge }) {
|
|||
const contract = new web3Home.eth.Contract(ForeignBridge.abi, foreignBridge)
|
||||
|
||||
console.log('\n[Foreign] Initializing Bridge Mediator with following parameters:')
|
||||
let homeFeeInWei = '0'
|
||||
let foreignFeeInWei = '0'
|
||||
if (FOREIGN_REWARDABLE) {
|
||||
homeFeeInWei = Web3Utils.toWei(HOME_TRANSACTIONS_FEE.toString(), 'ether')
|
||||
foreignFeeInWei = Web3Utils.toWei(FOREIGN_TRANSACTIONS_FEE.toString(), 'ether')
|
||||
}
|
||||
const rewardList = FOREIGN_MEDIATOR_REWARD_ACCOUNTS.split(' ')
|
||||
|
||||
const initializeData = await initializeMediator({
|
||||
contract,
|
||||
|
@ -104,9 +81,6 @@ async function initialize({ homeBridge, foreignBridge }) {
|
|||
minPerTx: FOREIGN_MIN_AMOUNT_PER_TX,
|
||||
executionDailyLimit: HOME_DAILY_LIMIT,
|
||||
executionMaxPerTx: HOME_MAX_AMOUNT_PER_TX,
|
||||
rewardAddressList: rewardList,
|
||||
homeToForeignFee: homeFeeInWei,
|
||||
foreignToHomeFee: foreignFeeInWei
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -22,6 +22,10 @@ const {
|
|||
HOME_BRIDGE_OWNER,
|
||||
HOME_UPGRADEABLE_ADMIN,
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY,
|
||||
HOME_REWARDABLE,
|
||||
HOME_TRANSACTIONS_FEE,
|
||||
FOREIGN_TRANSACTIONS_FEE,
|
||||
HOME_MEDIATOR_REWARD_ACCOUNTS
|
||||
} = require('../loadEnv')
|
||||
|
||||
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
|
||||
|
@ -38,7 +42,10 @@ async function initializeMediator({
|
|||
executionMaxPerTx,
|
||||
requestGasLimit,
|
||||
owner,
|
||||
tokenImage
|
||||
tokenImage,
|
||||
rewardAddressList,
|
||||
homeToForeignFee,
|
||||
foreignToHomeFee
|
||||
}
|
||||
}) {
|
||||
console.log(`
|
||||
|
@ -51,8 +58,15 @@ async function initializeMediator({
|
|||
EXECUTION_MAX_AMOUNT_PER_TX: ${executionMaxPerTx} which is ${Web3Utils.fromWei(executionMaxPerTx)} in eth,
|
||||
MEDIATOR_REQUEST_GAS_LIMIT : ${requestGasLimit},
|
||||
OWNER: ${owner},
|
||||
TOKEN_IMAGE: ${tokenImage}
|
||||
TOKEN_IMAGE: ${tokenImage},
|
||||
REWARD_ADDRESS_LIST: [${rewardAddressList.join(', ')}]
|
||||
`)
|
||||
if (HOME_REWARDABLE === 'BOTH_DIRECTIONS') {
|
||||
console.log(`
|
||||
HOME_TO_FOREIGN_FEE: ${homeToForeignFee} which is ${HOME_TRANSACTIONS_FEE * 100}%
|
||||
FOREIGN_TO_HOME_FEE: ${foreignToHomeFee} which is ${FOREIGN_TRANSACTIONS_FEE * 100}%
|
||||
`)
|
||||
}
|
||||
|
||||
return contract.methods
|
||||
.initialize(
|
||||
|
@ -62,7 +76,9 @@ async function initializeMediator({
|
|||
[executionDailyLimit.toString(), executionMaxPerTx.toString()],
|
||||
requestGasLimit,
|
||||
owner,
|
||||
tokenImage
|
||||
tokenImage,
|
||||
rewardAddressList,
|
||||
[homeToForeignFee.toString(), foreignToHomeFee.toString()]
|
||||
)
|
||||
.encodeABI()
|
||||
}
|
||||
|
@ -72,6 +88,13 @@ async function initialize({ homeBridge, foreignBridge, homeTokenImage }) {
|
|||
const mediatorContract = new web3Home.eth.Contract(HomeBridge.abi, homeBridge)
|
||||
|
||||
console.log('\n[Home] Initializing Bridge Mediator with following parameters:')
|
||||
let homeFeeInWei = '0'
|
||||
let foreignFeeInWei = '0'
|
||||
if (HOME_REWARDABLE === 'BOTH_DIRECTIONS') {
|
||||
homeFeeInWei = Web3Utils.toWei(HOME_TRANSACTIONS_FEE.toString(), 'ether')
|
||||
foreignFeeInWei = Web3Utils.toWei(FOREIGN_TRANSACTIONS_FEE.toString(), 'ether')
|
||||
}
|
||||
const rewardList = HOME_MEDIATOR_REWARD_ACCOUNTS.split(' ')
|
||||
|
||||
const initializeMediatorData = await initializeMediator({
|
||||
contract: mediatorContract,
|
||||
|
@ -85,7 +108,10 @@ async function initialize({ homeBridge, foreignBridge, homeTokenImage }) {
|
|||
minPerTx: HOME_MIN_AMOUNT_PER_TX,
|
||||
executionDailyLimit: FOREIGN_DAILY_LIMIT,
|
||||
executionMaxPerTx: FOREIGN_MAX_AMOUNT_PER_TX,
|
||||
tokenImage: homeTokenImage
|
||||
tokenImage: homeTokenImage,
|
||||
rewardAddressList: rewardList,
|
||||
homeToForeignFee: homeFeeInWei,
|
||||
foreignToHomeFee: foreignFeeInWei
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -115,9 +115,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// dailyLimit > maxPerTx
|
||||
|
@ -127,9 +125,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[maxPerTx, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// maxPerTx > minPerTx
|
||||
|
@ -139,9 +135,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, minPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// executionDailyLimit > executionMaxPerTx
|
||||
|
@ -151,9 +145,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionDailyLimit],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// maxGasPerTx > bridge maxGasPerTx
|
||||
|
@ -163,9 +155,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
twoEthers,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// not valid owner
|
||||
|
@ -175,9 +165,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
ZERO_ADDRESS,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
ZERO_ADDRESS
|
||||
).should.be.rejected
|
||||
|
||||
const { logs } = await contract.initialize(
|
||||
|
@ -186,9 +174,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.fulfilled
|
||||
|
||||
// already initialized
|
||||
|
@ -198,9 +184,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.rejected
|
||||
|
||||
// Then
|
||||
|
@ -243,9 +227,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[user2],
|
||||
[ether('0.1'), ZERO]
|
||||
owner
|
||||
).should.be.fulfilled
|
||||
})
|
||||
|
||||
|
@ -298,9 +280,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.fulfilled
|
||||
|
||||
const initialEvents = await getEvents(ambBridgeContract, { event: 'MockedEvent' })
|
||||
|
@ -620,30 +600,11 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
await token.mint(user, '1').should.be.fulfilled
|
||||
await token.transfer(contract.address, '1', { from: user }).should.be.fulfilled
|
||||
|
||||
expect(await contract.dailyLimit(token.address)).to.be.bignumber.equal('3')
|
||||
expect(await contract.maxPerTx(token.address)).to.be.bignumber.equal('2')
|
||||
expect(await contract.dailyLimit(token.address)).to.be.bignumber.equal('10000')
|
||||
expect(await contract.maxPerTx(token.address)).to.be.bignumber.equal('100')
|
||||
expect(await contract.minPerTx(token.address)).to.be.bignumber.equal('1')
|
||||
expect(await contract.executionDailyLimit(token.address)).to.be.bignumber.equal('3')
|
||||
expect(await contract.executionMaxPerTx(token.address)).to.be.bignumber.equal('2')
|
||||
})
|
||||
|
||||
it('should initialize fees', async () => {
|
||||
const HOME_TO_FOREIGN_FEE = await contract.HOME_TO_FOREIGN_FEE()
|
||||
const FOREIGN_TO_HOME_FEE = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await contract.setFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS, ether('0.01'))
|
||||
await contract.setFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS, ether('0.02'))
|
||||
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.02'))
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, token.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, token.address)).to.be.bignumber.equal(ZERO)
|
||||
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.02'))
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, token.address)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, token.address)).to.be.bignumber.equal(ether('0.02'))
|
||||
expect(await contract.executionDailyLimit(token.address)).to.be.bignumber.equal('10000')
|
||||
expect(await contract.executionMaxPerTx(token.address)).to.be.bignumber.equal('100')
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -692,6 +653,24 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(event[0].returnValues.value).to.be.equal(value.toString())
|
||||
expect(event[0].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
})
|
||||
|
||||
it('should not allow to use unregistered tokens', async () => {
|
||||
const otherToken = await ERC20Mock.new('Test', 'TST', 18)
|
||||
await otherToken.mint(contract.address, value)
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(otherToken.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
failedMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(failedMessageId)).to.be.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('requestFailedMessageFix', () => {
|
||||
|
@ -896,9 +875,7 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[owner],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
|
@ -911,7 +888,6 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(twoEthers)
|
||||
|
||||
await token.transfer(contract.address, halfEther, { from: user }).should.be.fulfilled
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), token.address, ether('0.1')).should.be.fulfilled
|
||||
await contract.setDailyLimit(token.address, ether('5')).should.be.fulfilled
|
||||
await contract.setMaxPerTx(token.address, ether('2')).should.be.fulfilled
|
||||
|
||||
|
@ -939,7 +915,6 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(twoEthers)
|
||||
|
||||
await token.transfer(contract.address, halfEther, { from: user }).should.be.fulfilled
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), token.address, ether('0.1')).should.be.fulfilled
|
||||
|
||||
expect(await contract.mediatorBalance(token.address)).to.be.bignumber.equal(halfEther)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(twoEthers.add(halfEther))
|
||||
|
@ -969,299 +944,4 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(events.length).to.be.equal(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('fees management', () => {
|
||||
beforeEach(async () => {
|
||||
await contract.initialize(
|
||||
ambBridgeContract.address,
|
||||
otherSideMediator.address,
|
||||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[owner],
|
||||
[ether('0.02'), ether('0.01')]
|
||||
).should.be.fulfilled
|
||||
|
||||
const initialEvents = await getEvents(ambBridgeContract, { event: 'MockedEvent' })
|
||||
expect(initialEvents.length).to.be.equal(0)
|
||||
})
|
||||
|
||||
it('change reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[8], { from: user }).should.be.rejected
|
||||
await contract.addRewardAddress(owner).should.be.rejected
|
||||
await contract.addRewardAddress(accounts[8]).should.be.fulfilled
|
||||
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[8], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
expect(await contract.isRewardAddress(owner)).to.be.equal(true)
|
||||
expect(await contract.isRewardAddress(accounts[8])).to.be.equal(true)
|
||||
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9], accounts[8], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('3')
|
||||
|
||||
await contract.removeRewardAddress(owner, { from: user }).should.be.rejected
|
||||
await contract.removeRewardAddress(accounts[7]).should.be.rejected
|
||||
await contract.removeRewardAddress(accounts[8]).should.be.fulfilled
|
||||
await contract.removeRewardAddress(accounts[8]).should.be.rejected
|
||||
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
expect(await contract.isRewardAddress(accounts[8])).to.be.equal(false)
|
||||
|
||||
await contract.removeRewardAddress(owner).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9]])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('1')
|
||||
expect(await contract.isRewardAddress(owner)).to.be.equal(false)
|
||||
|
||||
await contract.removeRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('0')
|
||||
expect(await contract.isRewardAddress(accounts[9])).to.be.equal(false)
|
||||
})
|
||||
|
||||
describe('update fee parameters', () => {
|
||||
it('should update default fee value', async () => {
|
||||
const feeType = await contract.HOME_TO_FOREIGN_FEE()
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: owner }).should.be.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS)).to.be.bignumber.equal(
|
||||
ether('0.01')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update default opposite direction fee value', async () => {
|
||||
const feeType = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: owner }).should.be.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.HOME_TO_FOREIGN_FEE(), ZERO_ADDRESS)).to.be.bignumber.equal(
|
||||
ether('0.02')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update fee value for registered token', async () => {
|
||||
const feeType = await contract.HOME_TO_FOREIGN_FEE()
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be.rejected
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, token.address)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.FOREIGN_TO_HOME_FEE(), token.address)).to.be.bignumber.equal(
|
||||
ether('0.01')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update opposite direction fee value for registered token', async () => {
|
||||
const feeType = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be.rejected
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, token.address)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.HOME_TO_FOREIGN_FEE(), token.address)).to.be.bignumber.equal(
|
||||
ether('0.02')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('distribute fee for home => foreign direction', async () => {
|
||||
beforeEach(async () => {
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(twoEthers)
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS, ZERO).should.be.fulfilled
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(oneEther)
|
||||
})
|
||||
|
||||
it('should collect and distribute 0% fee', async () => {
|
||||
await contract.setFee(await contract.HOME_TO_FOREIGN_FEE(), token.address, ZERO).should.be.fulfilled
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
|
||||
const event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
expect(event[0].returnValues.token).to.be.equal(token.address)
|
||||
expect(event[0].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[0].returnValues.value).to.be.equal(value.toString())
|
||||
expect(event[0].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(0)
|
||||
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ZERO)
|
||||
})
|
||||
|
||||
it('should collect and distribute 2% fee', async () => {
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
|
||||
const event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
expect(event[0].returnValues.token).to.be.equal(token.address)
|
||||
expect(event[0].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[0].returnValues.value).to.be.equal(ether('0.98').toString())
|
||||
expect(event[0].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(1)
|
||||
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(ether('1.98'))
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ether('0.02'))
|
||||
})
|
||||
|
||||
it('should collect and distribute 2% fee between two reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, ether('0.100000000000000050').toString(10))
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(token.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('0.100000000000000050')
|
||||
)
|
||||
|
||||
const event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(1)
|
||||
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(ether('1.098000000000000049'))
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ether('0.899999999999999950'))
|
||||
const balance1 = (await token.balanceOf(owner)).toString()
|
||||
const balance2 = (await token.balanceOf(accounts[9])).toString()
|
||||
expect(
|
||||
(balance1 === '1000000000000001' && balance2 === '1000000000000000') ||
|
||||
(balance1 === '1000000000000000' && balance2 === '1000000000000001')
|
||||
).to.be.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('distribute fee for foreign => home direction', async () => {
|
||||
beforeEach(async () => {
|
||||
await token.mint(user, twoEthers).should.be.fulfilled
|
||||
})
|
||||
|
||||
it('should collect and distribute 0% fee', async () => {
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS, ZERO).should.be.fulfilled
|
||||
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(value)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(twoEthers)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(0)
|
||||
})
|
||||
|
||||
it('should collect and distribute 1% fee', async () => {
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ether('0.99'))
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ether('0.01'))
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ether('1.98'))
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ether('0.02'))
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
})
|
||||
|
||||
it('should collect and distribute 1% fee between two reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, ether('0.200000000000000100'), { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('0.200000000000000100')
|
||||
)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ether('0.198000000000000099'))
|
||||
|
||||
const balance1 = (await token.balanceOf(owner)).toString()
|
||||
const balance2 = (await token.balanceOf(accounts[9])).toString()
|
||||
expect(
|
||||
(balance1 === '1000000000000001' && balance2 === '1000000000000000') ||
|
||||
(balance1 === '1000000000000000' && balance2 === '1000000000000001')
|
||||
).to.be.equal(true)
|
||||
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('1.200000000000000100')
|
||||
)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ether('1.188000000000000099'))
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -51,9 +51,7 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
owner
|
||||
)
|
||||
token = await ERC677BridgeToken.new('TEST', 'TST', 18)
|
||||
tokenImage = await PermittableToken.new('TEST', 'TST', 18, 1337)
|
||||
|
@ -137,7 +135,15 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(events.length).to.be.equal(1)
|
||||
expect(events[0].returnValues.foreignToken).to.be.equal(token.address)
|
||||
const homeToken = await PermittableToken.at(events[0].returnValues.homeToken)
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(value)
|
||||
const fee = await contract.getFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS)
|
||||
const rewardAccounts = (await contract.rewardAddressCount()).toNumber()
|
||||
const bridgedValue =
|
||||
rewardAccounts > 0
|
||||
? toBN(value)
|
||||
.mul(oneEther.sub(fee))
|
||||
.div(oneEther)
|
||||
: value
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(bridgedValue)
|
||||
return homeToken
|
||||
}
|
||||
|
||||
|
@ -165,7 +171,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// dailyLimit > maxPerTx
|
||||
|
@ -176,7 +184,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// maxPerTx > minPerTx
|
||||
|
@ -187,7 +197,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// executionDailyLimit > executionMaxPerTx
|
||||
|
@ -198,7 +210,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionDailyLimit],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// maxGasPerTx > bridge maxGasPerTx
|
||||
|
@ -209,7 +223,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
twoEthers,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// not valid owner
|
||||
|
@ -220,7 +236,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
ZERO_ADDRESS,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// token image is not a contract
|
||||
|
@ -231,7 +249,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
owner
|
||||
owner,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
const { logs } = await contract.initialize(
|
||||
|
@ -241,7 +261,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.fulfilled
|
||||
|
||||
// already initialized
|
||||
|
@ -252,7 +274,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.rejected
|
||||
|
||||
// Then
|
||||
|
@ -297,7 +321,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[user2],
|
||||
[ether('0.1'), ZERO]
|
||||
).should.be.fulfilled
|
||||
})
|
||||
|
||||
|
@ -344,7 +370,9 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address
|
||||
tokenImage.address,
|
||||
[],
|
||||
[ZERO, ZERO]
|
||||
).should.be.fulfilled
|
||||
|
||||
const initialEvents = await getEvents(ambBridgeContract, { event: 'MockedEvent' })
|
||||
|
@ -430,11 +458,30 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
token = await bridgeToken(token, '1')
|
||||
|
||||
expect(await token.decimals()).to.be.bignumber.equal('0')
|
||||
expect(await contract.dailyLimit(token.address)).to.be.bignumber.equal('3')
|
||||
expect(await contract.maxPerTx(token.address)).to.be.bignumber.equal('2')
|
||||
expect(await contract.dailyLimit(token.address)).to.be.bignumber.equal('10000')
|
||||
expect(await contract.maxPerTx(token.address)).to.be.bignumber.equal('100')
|
||||
expect(await contract.minPerTx(token.address)).to.be.bignumber.equal('1')
|
||||
expect(await contract.executionDailyLimit(token.address)).to.be.bignumber.equal('3')
|
||||
expect(await contract.executionMaxPerTx(token.address)).to.be.bignumber.equal('2')
|
||||
expect(await contract.executionDailyLimit(token.address)).to.be.bignumber.equal('10000')
|
||||
expect(await contract.executionMaxPerTx(token.address)).to.be.bignumber.equal('100')
|
||||
})
|
||||
|
||||
it('should initialize fees', async () => {
|
||||
const HOME_TO_FOREIGN_FEE = await contract.HOME_TO_FOREIGN_FEE()
|
||||
const FOREIGN_TO_HOME_FEE = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await contract.setFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS, ether('0.01'))
|
||||
await contract.setFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS, ether('0.02'))
|
||||
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.02'))
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, token.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, token.address)).to.be.bignumber.equal(ZERO)
|
||||
|
||||
const homeToken = await bridgeToken(token)
|
||||
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.02'))
|
||||
expect(await contract.getFee(HOME_TO_FOREIGN_FEE, homeToken.address)).to.be.bignumber.equal(ether('0.01'))
|
||||
expect(await contract.getFee(FOREIGN_TO_HOME_FEE, homeToken.address)).to.be.bignumber.equal(ether('0.02'))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -749,6 +796,22 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
expect(event[1].returnValues.value).to.be.equal(value.toString())
|
||||
expect(event[1].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
})
|
||||
|
||||
it('should not allow to use unregistered tokens', async () => {
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(homeToken.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
failedMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(failedMessageId)).to.be.equal(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('requestFailedMessageFix for token registration', () => {
|
||||
|
@ -950,4 +1013,349 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('fees management', () => {
|
||||
beforeEach(async () => {
|
||||
await contract.initialize(
|
||||
ambBridgeContract.address,
|
||||
otherSideMediator.address,
|
||||
[dailyLimit, maxPerTx, minPerTx],
|
||||
[executionDailyLimit, executionMaxPerTx],
|
||||
maxGasPerTx,
|
||||
owner,
|
||||
tokenImage.address,
|
||||
[owner],
|
||||
[ether('0.02'), ether('0.01')]
|
||||
).should.be.fulfilled
|
||||
|
||||
const initialEvents = await getEvents(ambBridgeContract, { event: 'MockedEvent' })
|
||||
expect(initialEvents.length).to.be.equal(0)
|
||||
})
|
||||
|
||||
it('change reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[8], { from: user }).should.be.rejected
|
||||
await contract.addRewardAddress(owner).should.be.rejected
|
||||
await contract.addRewardAddress(accounts[8]).should.be.fulfilled
|
||||
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[8], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
expect(await contract.isRewardAddress(owner)).to.be.equal(true)
|
||||
expect(await contract.isRewardAddress(accounts[8])).to.be.equal(true)
|
||||
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9], accounts[8], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('3')
|
||||
|
||||
await contract.removeRewardAddress(owner, { from: user }).should.be.rejected
|
||||
await contract.removeRewardAddress(accounts[7]).should.be.rejected
|
||||
await contract.removeRewardAddress(accounts[8]).should.be.fulfilled
|
||||
await contract.removeRewardAddress(accounts[8]).should.be.rejected
|
||||
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9], owner])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
expect(await contract.isRewardAddress(accounts[8])).to.be.equal(false)
|
||||
|
||||
await contract.removeRewardAddress(owner).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([accounts[9]])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('1')
|
||||
expect(await contract.isRewardAddress(owner)).to.be.equal(false)
|
||||
|
||||
await contract.removeRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressList()).to.be.eql([])
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('0')
|
||||
expect(await contract.isRewardAddress(accounts[9])).to.be.equal(false)
|
||||
})
|
||||
|
||||
describe('update fee parameters', () => {
|
||||
it('should update default fee value', async () => {
|
||||
const feeType = await contract.HOME_TO_FOREIGN_FEE()
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: owner }).should.be.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS)).to.be.bignumber.equal(
|
||||
ether('0.01')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update default opposite direction fee value', async () => {
|
||||
const feeType = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, ZERO_ADDRESS, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, ZERO_ADDRESS, ether('0.1'), { from: owner }).should.be.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, ZERO_ADDRESS)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.HOME_TO_FOREIGN_FEE(), ZERO_ADDRESS)).to.be.bignumber.equal(
|
||||
ether('0.02')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update fee value for registered token', async () => {
|
||||
const feeType = await contract.HOME_TO_FOREIGN_FEE()
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be.rejected
|
||||
|
||||
token = await bridgeToken(token)
|
||||
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, token.address)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.FOREIGN_TO_HOME_FEE(), token.address)).to.be.bignumber.equal(
|
||||
ether('0.01')
|
||||
)
|
||||
})
|
||||
|
||||
it('should update opposite direction fee value for registered token', async () => {
|
||||
const feeType = await contract.FOREIGN_TO_HOME_FEE()
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be.rejected
|
||||
token = await bridgeToken(token)
|
||||
await contract.setFee(feeType, token.address, ether('0.1'), { from: user }).should.be.rejected
|
||||
await contract.setFee(feeType, token.address, ether('1.1'), { from: owner }).should.be.rejected
|
||||
const { logs } = await contract.setFee(feeType, token.address, ether('0.1'), { from: owner }).should.be
|
||||
.fulfilled
|
||||
|
||||
expectEventInLogs(logs, 'FeeUpdated')
|
||||
expect(await contract.getFee(feeType, token.address)).to.be.bignumber.equal(ether('0.1'))
|
||||
expect(await contract.getFee(await contract.HOME_TO_FOREIGN_FEE(), token.address)).to.be.bignumber.equal(
|
||||
ether('0.02')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('distribute fee for foreign => home direction', async () => {
|
||||
beforeEach(async () => {
|
||||
await token.mint(user, twoEthers, { from: owner }).should.be.fulfilled
|
||||
expect(await token.balanceOf(user)).to.be.bignumber.equal(twoEthers)
|
||||
})
|
||||
|
||||
it('should collect and distribute 0% fee', async () => {
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS, ZERO).should.be.fulfilled
|
||||
const homeToken = await bridgeToken(token)
|
||||
|
||||
let event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
expect(event[0].returnValues.token).to.be.equal(homeToken.address)
|
||||
expect(event[0].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[0].returnValues.value).to.be.equal(value.toString())
|
||||
expect(event[0].returnValues.messageId).to.be.equal(deployMessageId)
|
||||
|
||||
let feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(0)
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
|
||||
event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(2)
|
||||
expect(event[1].returnValues.token).to.be.equal(homeToken.address)
|
||||
expect(event[1].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[1].returnValues.value).to.be.equal(value.toString())
|
||||
expect(event[1].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
|
||||
feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(0)
|
||||
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await homeToken.balanceOf(owner)).to.be.bignumber.equal(ZERO)
|
||||
})
|
||||
|
||||
it('should collect and distribute 1% fee', async () => {
|
||||
const homeToken = await bridgeToken(token)
|
||||
|
||||
let event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
expect(event[0].returnValues.token).to.be.equal(homeToken.address)
|
||||
expect(event[0].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[0].returnValues.value).to.be.equal(ether('0.99').toString())
|
||||
expect(event[0].returnValues.messageId).to.be.equal(deployMessageId)
|
||||
|
||||
let feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(1)
|
||||
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(ether('0.99'))
|
||||
expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await homeToken.balanceOf(owner)).to.be.bignumber.equal(ether('0.01'))
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, value.toString())
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
|
||||
event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(2)
|
||||
expect(event[1].returnValues.token).to.be.equal(homeToken.address)
|
||||
expect(event[1].returnValues.recipient).to.be.equal(user)
|
||||
expect(event[1].returnValues.value).to.be.equal(ether('0.99').toString())
|
||||
expect(event[1].returnValues.messageId).to.be.equal(exampleMessageId)
|
||||
|
||||
feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(ether('1.98'))
|
||||
expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await homeToken.balanceOf(owner)).to.be.bignumber.equal(ether('0.02'))
|
||||
})
|
||||
|
||||
it('should collect and distribute 1% fee between two reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
const homeToken = await bridgeToken(token, ether('0.200000000000000100'))
|
||||
|
||||
let event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(1)
|
||||
|
||||
let feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(1)
|
||||
|
||||
expect(await homeToken.balanceOf(user)).to.be.bignumber.equal(ether('0.198000000000000099'))
|
||||
expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
const balance1 = (await homeToken.balanceOf(owner)).toString()
|
||||
const balance2 = (await homeToken.balanceOf(accounts[9])).toString()
|
||||
expect(
|
||||
(balance1 === '1000000000000001' && balance2 === '1000000000000000') ||
|
||||
(balance1 === '1000000000000000' && balance2 === '1000000000000001')
|
||||
).to.be.equal(true)
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, ether('0.200000000000000100').toString(10))
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
expect(await contract.totalExecutedPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('0.400000000000000200')
|
||||
)
|
||||
|
||||
event = await getEvents(contract, { event: 'TokensBridged' })
|
||||
expect(event.length).to.be.equal(2)
|
||||
|
||||
feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('distribute fee for home => foreign direction', async () => {
|
||||
beforeEach(async () => {
|
||||
await contract.setFee(await contract.FOREIGN_TO_HOME_FEE(), ZERO_ADDRESS, ZERO).should.be.fulfilled
|
||||
const homeToken = await bridgeToken(token)
|
||||
|
||||
const data = await contract.contract.methods
|
||||
.handleBridgedTokens(token.address, user, value.toString(10))
|
||||
.encodeABI()
|
||||
|
||||
await ambBridgeContract.executeMessageCall(
|
||||
contract.address,
|
||||
otherSideMediator.address,
|
||||
data,
|
||||
exampleMessageId,
|
||||
1000000
|
||||
).should.be.fulfilled
|
||||
expect(await ambBridgeContract.messageCallStatus(exampleMessageId)).to.be.equal(true)
|
||||
|
||||
token = homeToken
|
||||
})
|
||||
|
||||
it('should collect and distribute 0% fee', async () => {
|
||||
await contract.setFee(await contract.HOME_TO_FOREIGN_FEE(), token.address, ZERO).should.be.fulfilled
|
||||
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(0)
|
||||
})
|
||||
|
||||
it('should collect and distribute 2% fee', async () => {
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ether('0.02'))
|
||||
await token.transfer(contract.address, value, { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(twoEthers)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
expect(await token.balanceOf(owner)).to.be.bignumber.equal(ether('0.04'))
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
})
|
||||
|
||||
it('should collect and distribute 2% fee between two reward addresses', async () => {
|
||||
await contract.addRewardAddress(accounts[9]).should.be.fulfilled
|
||||
expect(await contract.rewardAddressCount()).to.be.bignumber.equal('2')
|
||||
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(ZERO)
|
||||
await token.transfer(contract.address, ether('0.100000000000000050'), { from: user })
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('0.100000000000000050')
|
||||
)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
|
||||
const balance1 = (await token.balanceOf(owner)).toString()
|
||||
const balance2 = (await token.balanceOf(accounts[9])).toString()
|
||||
expect(
|
||||
(balance1 === '1000000000000001' && balance2 === '1000000000000000') ||
|
||||
(balance1 === '1000000000000000' && balance2 === '1000000000000001')
|
||||
).to.be.equal(true)
|
||||
|
||||
await token.transfer(contract.address, value, { from: user }).should.be.fulfilled
|
||||
expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(
|
||||
ether('1.100000000000000050')
|
||||
)
|
||||
expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(ZERO)
|
||||
|
||||
const feeEvents = await getEvents(contract, { event: 'FeeDistributed' })
|
||||
expect(feeEvents.length).to.be.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -7,6 +7,8 @@ const StakingTest = artifacts.require('Staking.sol')
|
|||
const HomeErcToErcBridge = artifacts.require('HomeBridgeErcToErc.sol')
|
||||
const ForeignNativeToErcBridge = artifacts.require('ForeignBridgeNativeToErc.sol')
|
||||
const BridgeValidators = artifacts.require('BridgeValidators.sol')
|
||||
const TokenProxy = artifacts.require('TokenProxy.sol')
|
||||
const PermittableTokenMock = artifacts.require('PermittableTokenMock.sol')
|
||||
|
||||
const { expect } = require('chai')
|
||||
const ethUtil = require('ethereumjs-util')
|
||||
|
@ -25,11 +27,10 @@ const executionMaxPerTx = halfEther
|
|||
const ZERO = new BN(0)
|
||||
const decimalShiftZero = 0
|
||||
|
||||
async function testERC677BridgeToken(accounts, rewardable) {
|
||||
async function testERC677BridgeToken(accounts, rewardable, permittable, createToken) {
|
||||
let token
|
||||
const owner = accounts[0]
|
||||
const user = accounts[1]
|
||||
const tokenContract = rewardable ? POA20RewardableMock : POA20
|
||||
|
||||
async function addBridge(token, bridge, options = { from: owner }) {
|
||||
if (rewardable) {
|
||||
|
@ -47,10 +48,10 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
|
||||
beforeEach(async () => {
|
||||
const args = ['POA ERC20 Foundation', 'POA20', 18]
|
||||
if (rewardable) {
|
||||
if (permittable) {
|
||||
args.push(100)
|
||||
}
|
||||
token = await tokenContract.new(...args)
|
||||
token = await createToken(args)
|
||||
})
|
||||
it('default values', async () => {
|
||||
expect(await token.symbol()).to.be.equal('POA20')
|
||||
|
@ -554,10 +555,10 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
const halfEther = ether('0.5')
|
||||
|
||||
const args = ['Roman Token', 'RST', 18]
|
||||
if (rewardable) {
|
||||
if (permittable) {
|
||||
args.push(100)
|
||||
}
|
||||
const tokenSecond = await tokenContract.new(...args)
|
||||
const tokenSecond = await createToken(args)
|
||||
|
||||
await tokenSecond.mint(accounts[0], halfEther).should.be.fulfilled
|
||||
halfEther.should.be.bignumber.equal(await tokenSecond.balanceOf(accounts[0]))
|
||||
|
@ -606,10 +607,10 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
})
|
||||
it('if transfer called on contract, still works even if onTokenTransfer doesnot exist', async () => {
|
||||
const args = ['Some', 'Token', 18]
|
||||
if (rewardable) {
|
||||
if (permittable) {
|
||||
args.push(100)
|
||||
}
|
||||
const someContract = await tokenContract.new(...args)
|
||||
const someContract = await createToken(args)
|
||||
await token.mint(user, '2', { from: owner }).should.be.fulfilled
|
||||
const tokenTransfer = await token.transfer(someContract.address, '1', { from: user }).should.be.fulfilled
|
||||
const tokenTransfer2 = await token.transfer(accounts[0], '1', { from: user }).should.be.fulfilled
|
||||
|
@ -625,7 +626,7 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
await token.renounceOwnership().should.be.rejectedWith(ERROR_MSG)
|
||||
})
|
||||
})
|
||||
if (rewardable) {
|
||||
if (permittable) {
|
||||
describe('permit', () => {
|
||||
const privateKey = '0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501210'
|
||||
const infinite = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)
|
||||
|
@ -690,7 +691,7 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
;(await token.allowance.call(holder, spender)).should.be.bignumber.equal(infinite)
|
||||
|
||||
// The caller of `permit` can't spend holder's funds
|
||||
await token.transferFrom(holder, accounts[9], '10000').should.be.rejectedWith(ERROR_MSG_OPCODE)
|
||||
await token.transferFrom(holder, accounts[9], '10000').should.be.rejected
|
||||
;(await token.balanceOf.call(holder)).should.be.bignumber.equal(new BN('10000'))
|
||||
|
||||
// Spender can transfer all holder's funds
|
||||
|
@ -823,9 +824,7 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
;(await token.expirations.call(holder, spender)).should.be.bignumber.equal(new BN('0'))
|
||||
|
||||
// Spender can't transfer the remaining holder's funds because of zero allowance
|
||||
await token
|
||||
.transferFrom(holder, accounts[9], '4000', { from: spender })
|
||||
.should.be.rejectedWith(ERROR_MSG_OPCODE)
|
||||
await token.transferFrom(holder, accounts[9], '4000', { from: spender }).should.be.rejected
|
||||
})
|
||||
it('should fail when invalid signature or parameters', async () => {
|
||||
let signature = permitSign(
|
||||
|
@ -912,9 +911,36 @@ async function testERC677BridgeToken(accounts, rewardable) {
|
|||
}
|
||||
|
||||
contract('ERC677BridgeToken', async accounts => {
|
||||
await testERC677BridgeToken(accounts, false)
|
||||
await testERC677BridgeToken(accounts, false, false, args => POA20.new(...args))
|
||||
})
|
||||
|
||||
contract('ERC677BridgeTokenRewardable', async accounts => {
|
||||
await testERC677BridgeToken(accounts, true)
|
||||
await testERC677BridgeToken(accounts, true, true, args => POA20RewardableMock.new(...args))
|
||||
})
|
||||
|
||||
contract('TokenProxy', async accounts => {
|
||||
const createToken = async args => {
|
||||
const impl = await PermittableTokenMock.new(...args)
|
||||
const proxy = await TokenProxy.new(impl.address, ...args)
|
||||
return PermittableTokenMock.at(proxy.address)
|
||||
}
|
||||
|
||||
await testERC677BridgeToken(accounts, false, true, createToken)
|
||||
|
||||
describe('constants', () => {
|
||||
let token
|
||||
beforeEach(async () => {
|
||||
token = await createToken(['POA ERC20 Foundation', 'POA20', 18, 100])
|
||||
})
|
||||
|
||||
it('should return version', async () => {
|
||||
expect(await token.version()).to.be.equal('1')
|
||||
})
|
||||
|
||||
it('should return PERMIT_TYPEHASH', async () => {
|
||||
expect(await token.PERMIT_TYPEHASH()).to.be.equal(
|
||||
'0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue