Avoid doublespending for transfers above limits (#509)

This commit is contained in:
Kirill Fedoseev 2020-10-19 23:14:12 +03:00 committed by GitHub
parent b22383f351
commit 2b51dcf0c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 447 additions and 193 deletions

View File

@ -2,42 +2,63 @@ pragma solidity 0.4.24;
import "../upgradeability/EternalStorage.sol";
/**
* @title BaseOverdrawManagement
* @dev This contract implements basic functionality for tracking execution bridge operations that are out of limits.
*/
contract BaseOverdrawManagement is EternalStorage {
event AmountLimitExceeded(address recipient, uint256 value, bytes32 transactionHash);
event AssetAboveLimitsFixed(bytes32 indexed transactionHash, uint256 value, uint256 remaining);
event MediatorAmountLimitExceeded(address recipient, uint256 value, bytes32 indexed messageId);
event AmountLimitExceeded(address recipient, uint256 value, bytes32 indexed transactionHash, bytes32 messageId);
event AssetAboveLimitsFixed(bytes32 indexed messageId, uint256 value, uint256 remaining);
bytes32 internal constant OUT_OF_LIMIT_AMOUNT = 0x145286dc85799b6fb9fe322391ba2d95683077b2adf34dd576dedc437e537ba7; // keccak256(abi.encodePacked("outOfLimitAmount"))
/**
* @dev Total amount coins/tokens that were bridged from the other side and are out of execution limits.
* @return total amount of all bridge operations above limits.
*/
function outOfLimitAmount() public view returns (uint256) {
return uintStorage[OUT_OF_LIMIT_AMOUNT];
}
function fixedAssets(bytes32 _txHash) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))];
}
/**
* @dev Internal function for updating a total amount that is out of execution limits.
* @param _value new value for the total amount of bridge operations above limits.
*/
function setOutOfLimitAmount(uint256 _value) internal {
uintStorage[OUT_OF_LIMIT_AMOUNT] = _value;
}
function txAboveLimits(bytes32 _txHash) internal view returns (address recipient, uint256 value) {
recipient = addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _txHash))];
value = uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _txHash))];
/**
* @dev Internal function for retrieving information about out-of-limits bridge operation.
* @param _messageId id of the message that cause above-limits error.
* @return (address of the receiver, amount of coins/tokens in the bridge operation)
*/
function txAboveLimits(bytes32 _messageId) internal view returns (address recipient, uint256 value) {
recipient = addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _messageId))];
value = uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _messageId))];
}
function setTxAboveLimits(address _recipient, uint256 _value, bytes32 _txHash) internal {
addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _txHash))] = _recipient;
setTxAboveLimitsValue(_value, _txHash);
/**
* @dev Internal function for updating information about tbe out-of-limits bridge operation.
* @param _recipient receiver specified in the bridge operation.
* @param _value amount of coins/tokens inside the bridge operation.
* @param _messageId id of the message that cause above-limits error.
*/
function setTxAboveLimits(address _recipient, uint256 _value, bytes32 _messageId) internal {
addressStorage[keccak256(abi.encodePacked("txOutOfLimitRecipient", _messageId))] = _recipient;
setTxAboveLimitsValue(_value, _messageId);
}
function setTxAboveLimitsValue(uint256 _value, bytes32 _txHash) internal {
uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _txHash))] = _value;
}
function setFixedAssets(bytes32 _txHash) internal {
boolStorage[keccak256(abi.encodePacked("fixedAssets", _txHash))] = true;
/**
* @dev Internal function for updating information about the remaining value of out-of-limits bridge operation.
* @param _value amount of coins/tokens inside the bridge operation.
* @param _messageId id of the message that cause above-limits error.
*/
function setTxAboveLimitsValue(uint256 _value, bytes32 _messageId) internal {
uintStorage[keccak256(abi.encodePacked("txOutOfLimitValue", _messageId))] = _value;
}
/* solcov ignore next */
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign, uint256 valueToUnlock) external;
function fixAssetsAboveLimits(bytes32 messageId, bool unlockOnForeign, uint256 valueToUnlock) external;
}

View File

@ -8,6 +8,10 @@ import "../libraries/Message.sol";
import "./BasicBridge.sol";
import "./BasicTokenBridge.sol";
/**
* @title BasicHomeBridge
* @dev This contract implements common functionality for all vanilla bridge modes on the Home side.
*/
contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicTokenBridge {
using SafeMath for uint256;
@ -21,9 +25,16 @@ contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicToken
uint256 NumberOfCollectedSignatures
);
/**
* @dev Executes a message affirmation for some Foreign side event.
* Can be called only by a current bridge validator.
* @param recipient tokens/coins of receiver address, where the assets should be unlocked/minted.
* @param value amount of assets to unlock/mint.
* @param transactionHash reference event transaction hash on the Foreign side of the bridge.
*/
function executeAffirmation(address recipient, uint256 value, bytes32 transactionHash) external onlyValidator {
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
if (withinExecutionLimit(value)) {
bytes32 hashMsg = keccak256(abi.encodePacked(recipient, value, transactionHash));
bytes32 hashSender = keccak256(abi.encodePacked(msg.sender, hashMsg));
// Duplicated affirmations
require(!affirmationsSigned(hashSender));
@ -43,12 +54,12 @@ contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicToken
// it will couse funds lock on the home side of the bridge
setNumAffirmationsSigned(hashMsg, markAsProcessed(signed));
if (value > 0) {
require(onExecuteAffirmation(recipient, value, transactionHash));
require(onExecuteAffirmation(recipient, value, transactionHash, hashMsg));
}
emit AffirmationCompleted(recipient, value, transactionHash);
}
} else {
onFailedAffirmation(recipient, value, transactionHash);
onFailedAffirmation(recipient, value, transactionHash, hashMsg);
}
}
@ -92,7 +103,10 @@ contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicToken
}
/* solcov ignore next */
function onExecuteAffirmation(address, uint256, bytes32) internal returns (bool);
function onExecuteAffirmation(address, uint256, bytes32, bytes32) internal returns (bool);
/* solcov ignore next */
function onFailedAffirmation(address, uint256, bytes32, bytes32) internal;
/* solcov ignore next */
function onSignaturesCollected(bytes) internal;
@ -153,7 +167,4 @@ contract BasicHomeBridge is EternalStorage, Validatable, BasicBridge, BasicToken
function requiredMessageLength() public pure returns (uint256) {
return Message.requiredMessageLength();
}
/* solcov ignore next */
function onFailedAffirmation(address, uint256, bytes32) internal;
}

View File

@ -0,0 +1,64 @@
pragma solidity 0.4.24;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./Upgradeable.sol";
import "./RewardableBridge.sol";
import "./BasicHomeBridge.sol";
import "./BaseOverdrawManagement.sol";
/**
* @title HomeOverdrawManagement
* @dev This contract implements functionality for recovering from out-of-limits executions in Home side vanilla bridges.
*/
contract HomeOverdrawManagement is BaseOverdrawManagement, RewardableBridge, Upgradeable, BasicHomeBridge {
using SafeMath for uint256;
/**
* @dev Fixes locked tokens, that were out of execution limits during the call to executeAffirmation.
* @param hashMsg reference for bridge operation that was out of execution limits.
* @param unlockOnForeign true if fixed tokens should be unlocked to the other side of the bridge.
* @param valueToUnlock unlocked amount of tokens, should be less than txAboveLimitsValue.
* Should be less than maxPerTx(), if tokens need to be unlocked on the other side.
*/
function fixAssetsAboveLimits(bytes32 hashMsg, bool unlockOnForeign, uint256 valueToUnlock)
external
onlyIfUpgradeabilityOwner
{
uint256 signed = numAffirmationsSigned(hashMsg);
require(!isAlreadyProcessed(signed));
(address recipient, uint256 value) = txAboveLimits(hashMsg);
require(recipient != address(0) && value > 0 && value >= valueToUnlock);
setOutOfLimitAmount(outOfLimitAmount().sub(valueToUnlock));
uint256 pendingValue = value.sub(valueToUnlock);
setTxAboveLimitsValue(pendingValue, hashMsg);
emit AssetAboveLimitsFixed(hashMsg, valueToUnlock, pendingValue);
if (unlockOnForeign) {
require(valueToUnlock <= maxPerTx());
address feeManager = feeManagerContract();
uint256 eventValue = valueToUnlock;
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToUnlock, false, feeManager, HOME_FEE);
eventValue = valueToUnlock.sub(fee);
}
emit UserRequestForSignature(recipient, eventValue);
}
}
/**
* @dev Internal function for clearing above limits markers for some failed transfer.
* Useful when transfer is being reprocessed on a new day or after limits were updated.
* It is required that fixAssetsAboveLimits was not called on the failed transfer before prior to this function.
* @param _hashMsg hash of the message, works as a unique indentifier.
* @param _value transferred amount of tokens/coins in the fixed message.
*/
function _clearAboveLimitsMarker(bytes32 _hashMsg, uint256 _value) internal {
(address aboveLimitsRecipient, uint256 aboveLimitsValue) = txAboveLimits(_hashMsg);
// check if transfer was marked as out of limits
if (aboveLimitsRecipient != address(0)) {
// revert if a given transaction hash was already processed by the call to fixAssetsAboveLimits
require(aboveLimitsValue == _value);
setTxAboveLimits(address(0), 0, _hashMsg);
setOutOfLimitAmount(outOfLimitAmount().sub(_value));
}
}
}

View File

@ -1,41 +0,0 @@
pragma solidity 0.4.24;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./Upgradeable.sol";
import "./RewardableBridge.sol";
import "./BasicTokenBridge.sol";
import "./BaseOverdrawManagement.sol";
contract OverdrawManagement is BaseOverdrawManagement, RewardableBridge, Upgradeable, BasicTokenBridge {
using SafeMath for uint256;
event UserRequestForSignature(address recipient, uint256 value);
function fixAssetsAboveLimits(bytes32 txHash, bool unlockOnForeign, uint256 valueToUnlock)
external
onlyIfUpgradeabilityOwner
{
require(!fixedAssets(txHash));
require(valueToUnlock <= maxPerTx());
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(txHash);
require(recipient != address(0) && value > 0 && value >= valueToUnlock);
setOutOfLimitAmount(outOfLimitAmount().sub(valueToUnlock));
uint256 pendingValue = value.sub(valueToUnlock);
setTxAboveLimitsValue(pendingValue, txHash);
emit AssetAboveLimitsFixed(txHash, valueToUnlock, pendingValue);
if (pendingValue == 0) {
setFixedAssets(txHash);
}
if (unlockOnForeign) {
address feeManager = feeManagerContract();
uint256 eventValue = valueToUnlock;
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToUnlock, false, feeManager, HOME_FEE);
eventValue = valueToUnlock.sub(fee);
}
emit UserRequestForSignature(recipient, eventValue);
}
}
}

View File

@ -116,33 +116,28 @@ contract BasicAMBErc677ToErc677 is
require(recipient == address(0) && value == 0);
setOutOfLimitAmount(outOfLimitAmount().add(_value));
setTxAboveLimits(_recipient, _value, _messageId);
emit AmountLimitExceeded(_recipient, _value, _messageId);
emit MediatorAmountLimitExceeded(_recipient, _value, _messageId);
}
/**
* @dev Fixes locked tokens, that were out of execution limits during the call to handleBridgedTokens
* @param messageId reference for bridge operation that was out of execution limits
* @param unlockOnForeign true if fixed tokens should be unlocked to the other side of the bridge
* @param valueToUnlock unlocked amount of tokens, should be less than maxPerTx() and saved txAboveLimitsValue
* @param unlockOnOtherSide true if fixed tokens should be unlocked to the other side of the bridge
* @param valueToUnlock unlocked amount of tokens, should be less than saved txAboveLimitsValue.
* Should be less than maxPerTx(), if tokens need to be unlocked on the other side.
*/
function fixAssetsAboveLimits(bytes32 messageId, bool unlockOnForeign, uint256 valueToUnlock)
function fixAssetsAboveLimits(bytes32 messageId, bool unlockOnOtherSide, uint256 valueToUnlock)
external
onlyIfUpgradeabilityOwner
{
require(!fixedAssets(messageId));
require(valueToUnlock <= maxPerTx());
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(messageId);
(address recipient, uint256 value) = txAboveLimits(messageId);
require(recipient != address(0) && value > 0 && value >= valueToUnlock);
setOutOfLimitAmount(outOfLimitAmount().sub(valueToUnlock));
uint256 pendingValue = value.sub(valueToUnlock);
setTxAboveLimitsValue(pendingValue, messageId);
emit AssetAboveLimitsFixed(messageId, valueToUnlock, pendingValue);
if (pendingValue == 0) {
setFixedAssets(messageId);
}
if (unlockOnForeign) {
if (unlockOnOtherSide) {
require(valueToUnlock <= maxPerTx());
passMessage(recipient, recipient, valueToUnlock);
}
}

View File

@ -4,15 +4,20 @@ import "../../libraries/Message.sol";
import "../../upgradeability/EternalStorage.sol";
import "../../interfaces/IBurnableMintableERC677Token.sol";
import "../BasicHomeBridge.sol";
import "../OverdrawManagement.sol";
import "../HomeOverdrawManagement.sol";
import "./RewardableHomeBridgeErcToErc.sol";
import "../ERC677BridgeForBurnableMintableToken.sol";
/**
* @title HomeBridgeErcToErc
* @dev This contract Home side logic for the erc-to-erc vanilla bridge mode.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract HomeBridgeErcToErc is
EternalStorage,
BasicHomeBridge,
ERC677BridgeForBurnableMintableToken,
OverdrawManagement,
HomeOverdrawManagement,
RewardableHomeBridgeErcToErc
{
function initialize(
@ -134,13 +139,26 @@ contract HomeBridgeErcToErc is
return 0xba4690f5; // bytes4(keccak256(abi.encodePacked("erc-to-erc-core")))
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) {
/**
* @dev Internal callback to be called on successfull message execution.
* Should be called only after enough affirmations from the validators are already collected.
* @param _recipient address of the receiver where the new tokens should be minted.
* @param _value amount of tokens to mint.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
* @return true, if execution completed successfully.
*/
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg)
internal
returns (bool)
{
_clearAboveLimitsMarker(_hashMsg, _value);
addTotalExecutedPerDay(getCurrentDay(), _value);
uint256 valueToMint = _shiftValue(_value);
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToMint, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
distributeFeeFromAffirmation(fee, feeManager, _txHash);
valueToMint = valueToMint.sub(fee);
}
return IBurnableMintableERC677Token(erc677token()).mint(_recipient, valueToMint);
@ -169,13 +187,19 @@ contract HomeBridgeErcToErc is
}
}
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash) internal {
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(_txHash);
/**
* @dev Internal callback to be called on failed message execution due to the out-of-limits error.
* This function saves the bridge operation information for further processing.
* @param _recipient address of the receiver where the new tokens should be minted.
* @param _value amount of tokens to mint.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
*/
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg) internal {
(address recipient, uint256 value) = txAboveLimits(_hashMsg);
require(recipient == address(0) && value == 0);
setOutOfLimitAmount(outOfLimitAmount().add(_value));
setTxAboveLimits(_recipient, _value, _txHash);
emit AmountLimitExceeded(_recipient, _value, _txHash);
setTxAboveLimits(_recipient, _value, _hashMsg);
emit AmountLimitExceeded(_recipient, _value, _txHash, _hashMsg);
}
}

View File

@ -4,14 +4,19 @@ import "../../libraries/Message.sol";
import "../../upgradeability/EternalStorage.sol";
import "../../interfaces/IBlockReward.sol";
import "../BasicHomeBridge.sol";
import "../OverdrawManagement.sol";
import "../HomeOverdrawManagement.sol";
import "./RewardableHomeBridgeErcToNative.sol";
import "../BlockRewardBridge.sol";
/**
* @title HomeBridgeErcToNative
* @dev This contract Home side logic for the erc-to-native vanilla bridge mode.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract HomeBridgeErcToNative is
EternalStorage,
BasicHomeBridge,
OverdrawManagement,
HomeOverdrawManagement,
RewardableHomeBridgeErcToNative,
BlockRewardBridge
{
@ -158,7 +163,20 @@ contract HomeBridgeErcToNative is
_setOwner(_owner);
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) {
/**
* @dev Internal callback to be called on successfull message execution.
* Should be called only after enough affirmations from the validators are already collected.
* @param _recipient address of the receiver where the new coins should be minted.
* @param _value amount of coins to mint.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
* @return true, if execution completed successfully.
*/
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg)
internal
returns (bool)
{
_clearAboveLimitsMarker(_hashMsg, _value);
addTotalExecutedPerDay(getCurrentDay(), _value);
IBlockReward blockReward = blockRewardContract();
require(blockReward != address(0));
@ -166,7 +184,7 @@ contract HomeBridgeErcToNative is
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToMint, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
distributeFeeFromAffirmation(fee, feeManager, _txHash);
valueToMint = valueToMint.sub(fee);
}
blockReward.addExtraReceiver(valueToMint, _recipient);
@ -190,13 +208,19 @@ contract HomeBridgeErcToNative is
uintStorage[TOTAL_BURNT_COINS] = _amount;
}
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash) internal {
address recipient;
uint256 value;
(recipient, value) = txAboveLimits(_txHash);
/**
* @dev Internal callback to be called on failed message execution due to the out-of-limits error.
* This function saves the bridge operation information for further processing.
* @param _recipient address of the receiver where the new coins should be minted.
* @param _value amount of coins to mint.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
*/
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg) internal {
(address recipient, uint256 value) = txAboveLimits(_hashMsg);
require(recipient == address(0) && value == 0);
setOutOfLimitAmount(outOfLimitAmount().add(_value));
setTxAboveLimits(_recipient, _value, _txHash);
emit AmountLimitExceeded(_recipient, _value, _txHash);
setTxAboveLimits(_recipient, _value, _hashMsg);
emit AmountLimitExceeded(_recipient, _value, _txHash, _hashMsg);
}
}

View File

@ -6,6 +6,11 @@ import "../BasicHomeBridge.sol";
import "./RewardableHomeBridgeNativeToErc.sol";
import "../../libraries/Address.sol";
/**
* @title HomeBridgeNativeToErc
* @dev This contract Home side logic for the native-to-erc vanilla bridge mode.
* It is designed to be used as an implementation contract of EternalStorageProxy contract.
*/
contract HomeBridgeNativeToErc is EternalStorage, BasicHomeBridge, RewardableHomeBridgeNativeToErc {
function() public payable {
require(msg.data.length == 0);
@ -132,14 +137,27 @@ contract HomeBridgeNativeToErc is EternalStorage, BasicHomeBridge, RewardableHom
}
}
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 txHash) internal returns (bool) {
/**
* @dev Internal callback to be called on successfull message execution.
* Should be called only after enough affirmations from the validators are already collected.
* @param _recipient address of the receiver where the new coins should be unlocked.
* @param _value amount of coins to unlock.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
* Not used in this bridge mode, but required for interface unification with other bridge modes.
* @return true, if execution completed successfully.
*/
function onExecuteAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg)
internal
returns (bool)
{
addTotalExecutedPerDay(getCurrentDay(), _value);
uint256 valueToTransfer = _shiftValue(_value);
address feeManager = feeManagerContract();
if (feeManager != address(0)) {
uint256 fee = calculateFee(valueToTransfer, false, feeManager, FOREIGN_FEE);
distributeFeeFromAffirmation(fee, feeManager, txHash);
distributeFeeFromAffirmation(fee, feeManager, _txHash);
valueToTransfer = valueToTransfer.sub(fee);
}
@ -147,11 +165,16 @@ contract HomeBridgeNativeToErc is EternalStorage, BasicHomeBridge, RewardableHom
return true;
}
function onFailedAffirmation(
address, /*_recipient*/
uint256, /*_value*/
bytes32 /*_txHash*/
) internal {
/**
* @dev Internal callback to be called on failed message execution due to the out-of-limits error.
* This function saves the bridge operation information for further processing.
* @param _recipient address of the receiver where the new coins should be unlocked.
* @param _value amount of coins to unlock.
* @param _txHash reference transaction hash on the Foreign side of the bridge which cause this operation.
* @param _hashMsg unique identifier of the particular bridge operation.
*/
function onFailedAffirmation(address _recipient, uint256 _value, bytes32 _txHash, bytes32 _hashMsg) internal {
// solhint-disable-previous-line no-unused-vars
revert();
}

View File

@ -391,21 +391,21 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
1000000
).should.be.fulfilled
const outOfLimitEvent = await getEvents(contract, { event: 'AmountLimitExceeded' })
const outOfLimitEvent = await getEvents(contract, { event: 'MediatorAmountLimitExceeded' })
expect(outOfLimitEvent.length).to.be.equal(1)
expect(outOfLimitEvent[0].returnValues.recipient).to.be.equal(user)
expect(outOfLimitEvent[0].returnValues.value).to.be.equal(twoEthers.toString())
expect(outOfLimitEvent[0].returnValues.transactionHash).to.be.equal(exampleMessageId)
expect(outOfLimitEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
})
it('Should revert if value to unlock is bigger than max per transaction', async function() {
await contract.fixAssetsAboveLimits(exampleMessageId, false, twoEthers).should.be.rejectedWith(ERROR_MSG)
await contract.fixAssetsAboveLimits(exampleMessageId, true, twoEthers).should.be.rejectedWith(ERROR_MSG)
})
it('Should allow to partially reduce outOfLimitAmount and not emit amb event', async function() {
const { logs } = await contract.fixAssetsAboveLimits(exampleMessageId, false, halfEther).should.be.fulfilled
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: ether('1.5')
})
@ -418,7 +418,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: oneEther
})
@ -431,7 +431,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: ether('1.5')
})
@ -444,7 +444,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: oneEther
})
@ -457,7 +457,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: ether('1.5')
})
@ -467,7 +467,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: oneEther
})
@ -477,7 +477,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logsThirdTx.length.should.be.equal(1)
expectEventInLogs(logsThirdTx, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: halfEther,
remaining: halfEther
})
@ -489,7 +489,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: oneEther,
remaining: oneEther
})
@ -499,7 +499,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash: exampleMessageId,
messageId: exampleMessageId,
value: oneEther,
remaining: ZERO
})

View File

@ -255,7 +255,7 @@ contract('ForeignAMBErc677ToErc677', async accounts => {
expect(TokensBridgedEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
})
}
it('should emit AmountLimitExceeded and not transfer tokens when out of execution limits', async () => {
it('should emit MediatorAmountLimitExceeded and not transfer tokens when out of execution limits', async () => {
// Given
const currentDay = await foreignBridge.getCurrentDay()
expect(await foreignBridge.totalExecutedPerDay(currentDay)).to.be.bignumber.equal(ZERO)
@ -285,11 +285,11 @@ contract('ForeignAMBErc677ToErc677', async accounts => {
expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(ZERO)
expect(await foreignBridge.outOfLimitAmount()).to.be.bignumber.equal(twoEthers)
const outOfLimitEvent = await getEvents(foreignBridge, { event: 'AmountLimitExceeded' })
const outOfLimitEvent = await getEvents(foreignBridge, { event: 'MediatorAmountLimitExceeded' })
expect(outOfLimitEvent.length).to.be.equal(1)
expect(outOfLimitEvent[0].returnValues.recipient).to.be.equal(user)
expect(outOfLimitEvent[0].returnValues.value).to.be.equal(twoEthers.toString())
expect(outOfLimitEvent[0].returnValues.transactionHash).to.be.equal(exampleMessageId)
expect(outOfLimitEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
const TokensBridgedEvent = await getEvents(foreignBridge, { event: 'TokensBridged' })
expect(TokensBridgedEvent.length).to.be.equal(0)

View File

@ -261,7 +261,7 @@ contract('HomeAMBErc677ToErc677', async accounts => {
expect(TokensBridgedEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
})
}
it('should emit AmountLimitExceeded and not mint tokens when out of execution limits', async () => {
it('should emit MediatorAmountLimitExceeded and not mint tokens when out of execution limits', async () => {
// Given
const currentDay = await homeBridge.getCurrentDay()
expect(await homeBridge.totalExecutedPerDay(currentDay)).to.be.bignumber.equal(ZERO)
@ -292,11 +292,11 @@ contract('HomeAMBErc677ToErc677', async accounts => {
expect(await erc677Token.balanceOf(user)).to.be.bignumber.equal(ZERO)
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(twoEthers)
const outOfLimitEvent = await getEvents(homeBridge, { event: 'AmountLimitExceeded' })
const outOfLimitEvent = await getEvents(homeBridge, { event: 'MediatorAmountLimitExceeded' })
expect(outOfLimitEvent.length).to.be.equal(1)
expect(outOfLimitEvent[0].returnValues.recipient).to.be.equal(user)
expect(outOfLimitEvent[0].returnValues.value).to.be.equal(twoEthers.toString())
expect(outOfLimitEvent[0].returnValues.transactionHash).to.be.equal(exampleMessageId)
expect(outOfLimitEvent[0].returnValues.messageId).to.be.equal(exampleMessageId)
const TokensBridgedEvent = await getEvents(homeBridge, { event: 'TokensBridged' })
expect(TokensBridgedEvent.length).to.be.equal(0)

View File

@ -600,12 +600,13 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
transactionHash
})
// should fail for the same message
await homeBridge
.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] })
.should.be.rejectedWith(ERROR_MSG)
await homeBridge
.executeAffirmation(accounts[6], value, transactionHash, { from: authorities[0] })
.should.be.rejectedWith(ERROR_MSG)
// should succeed for different transfer in the same message
await homeBridge.executeAffirmation(accounts[6], value, transactionHash, { from: authorities[0] }).should.be
.fulfilled
})
it('should not allow execute affirmation over daily foreign limit', async () => {
const recipient = accounts[5]
@ -869,6 +870,7 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
const storageProxy = await EternalStorageProxy.new().should.be.fulfilled
await storageProxy.upgradeTo('1', homeBridgeImpl.address).should.be.fulfilled
homeBridge = await HomeBridge.at(storageProxy.address)
token = await ERC677BridgeToken.new('TEST', 'TST', 18)
await homeBridge.initialize(
validatorContract.address,
[oneEther, halfEther, minPerTx],
@ -879,6 +881,7 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
owner,
decimalShiftZero
).should.be.fulfilled
await token.transferOwnership(homeBridge.address).should.be.fulfilled
})
it('Should revert if value to unlock is bigger than max per transaction', async () => {
const recipient = accounts[5]
@ -889,11 +892,13 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(transactionHash, false, value).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId, true, value).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.fulfilled
})
it('Should allow to partially reduce outOfLimitAmount and not emit UserRequestForSignature', async () => {
const recipient = accounts[5]
@ -904,26 +909,27 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: ZERO
})
@ -938,15 +944,16 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -957,12 +964,12 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(2)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: ZERO
})
@ -982,15 +989,16 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -1001,12 +1009,12 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, quarterEther).should
.be.fulfilled
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(2)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: quarterEther,
remaining: quarterEther
})
@ -1017,11 +1025,11 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(quarterEther)
await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.rejectedWith(ERROR_MSG)
const { logs: logsThirdTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, quarterEther).should.be
await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.rejectedWith(ERROR_MSG)
const { logs: logsThirdTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be
.fulfilled
expectEventInLogs(logsThirdTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: quarterEther,
remaining: ZERO
})
@ -1038,36 +1046,39 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const transactionHash2 = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
await homeBridge.executeAffirmation(recipient, value, transactionHash, {
const { logs: logs1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash2, {
const { logs: logs2 } = await homeBridge.executeAffirmation(recipient, value, transactionHash2, {
from: authorities[0]
}).should.be.fulfilled
const messageId1 = logs1[0].args.messageId
const messageId2 = logs2[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value.add(value))
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled
const newOutOfLimitAmount = await homeBridge.outOfLimitAmount()
newOutOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO)
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.rejectedWith(ERROR_MSG)
})
it('Should fail if txHash didnt increase out of limit amount', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const invalidTxHash = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
const invalidMessageId = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
@ -1075,7 +1086,7 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
await homeBridge.fixAssetsAboveLimits(invalidTxHash, true, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(invalidMessageId, true, halfEther).should.be.rejectedWith(ERROR_MSG)
})
it('Should fail if not called by proxyOwner', async () => {
const recipient = accounts[5]
@ -1087,11 +1098,12 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
await homeBridge
.fixAssetsAboveLimits(transactionHash, true, halfEther, { from: recipient })
.fixAssetsAboveLimits(messageId, true, halfEther, { from: recipient })
.should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther, { from: owner }).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther, { from: owner }).should.be.fulfilled
})
it('Should emit UserRequestForSignature with value reduced by fee', async () => {
const recipient = accounts[5]
@ -1115,15 +1127,16 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
value,
transactionHash
})
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -1132,6 +1145,61 @@ contract('HomeBridge_ERC20_to_ERC20', async accounts => {
value: finalValue
})
})
it('should not allow to fix assets after they were already processed', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.fulfilled
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(ZERO)
await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.rejectedWith(ERROR_MSG)
})
it('should not allow to executeAffirmation after assets were fixed', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.rejected
await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.rejected
})
})
describe('#claimTokens', () => {
it('should be able to call claimTokens on tokenAddress', async () => {

View File

@ -1135,12 +1135,13 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
transactionHash
})
// should fail for the same message
await homeBridge
.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] })
.should.be.rejectedWith(ERROR_MSG)
await homeBridge
.executeAffirmation(accounts[6], value, transactionHash, { from: authorities[0] })
.should.be.rejectedWith(ERROR_MSG)
// should succeed for different transfer in the same message
await homeBridge.executeAffirmation(accounts[6], value, transactionHash, { from: authorities[0] }).should.be
.fulfilled
})
it('should not allow execute affirmation over daily foreign limit', async () => {
await blockRewardContract.sendTransaction({
@ -1414,11 +1415,13 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(transactionHash, false, value).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId, true, value).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.fulfilled
})
it('Should allow to partially reduce outOfLimitAmount and not emit UserRequestForSignature', async () => {
const recipient = accounts[5]
@ -1429,26 +1432,27 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
logs.length.should.be.equal(1)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(1)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: ZERO
})
@ -1463,15 +1467,16 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -1482,12 +1487,12 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(2)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: ZERO
})
@ -1507,15 +1512,16 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -1526,12 +1532,12 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(halfEther)
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, quarterEther).should
.be.fulfilled
const { logs: logsSecondTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be
.fulfilled
logsSecondTx.length.should.be.equal(2)
expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: quarterEther,
remaining: quarterEther
})
@ -1542,11 +1548,11 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(quarterEther)
await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.rejectedWith(ERROR_MSG)
const { logs: logsThirdTx } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, quarterEther).should.be
await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.rejectedWith(ERROR_MSG)
const { logs: logsThirdTx } = await homeBridge.fixAssetsAboveLimits(messageId, true, quarterEther).should.be
.fulfilled
expectEventInLogs(logsThirdTx, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: quarterEther,
remaining: ZERO
})
@ -1563,36 +1569,38 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const transactionHash2 = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
await homeBridge.executeAffirmation(recipient, value, transactionHash, {
const { logs: logs1 } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash2, {
const { logs: logs2 } = await homeBridge.executeAffirmation(recipient, value, transactionHash2, {
from: authorities[0]
}).should.be.fulfilled
const messageId1 = logs1[0].args.messageId
const messageId2 = logs2[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value.add(value))
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.fulfilled
const newOutOfLimitAmount = await homeBridge.outOfLimitAmount()
newOutOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(transactionHash, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId1, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.fulfilled
expect(await homeBridge.outOfLimitAmount()).to.be.bignumber.equal(ZERO)
await homeBridge.fixAssetsAboveLimits(transactionHash2, false, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(messageId2, false, halfEther).should.be.rejectedWith(ERROR_MSG)
})
it('Should fail if txHash didnt increase out of limit amount', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const invalidTxHash = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
const invalidMessageId = '0x35d3818e50234655f6aebb2a1cfbf30f59568d8a4ec72066fac5a25dbe7b8121'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
@ -1600,7 +1608,7 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
await homeBridge.fixAssetsAboveLimits(invalidTxHash, true, halfEther).should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(invalidMessageId, true, halfEther).should.be.rejectedWith(ERROR_MSG)
})
it('Should fail if not called by proxyOwner', async () => {
const recipient = accounts[5]
@ -1612,11 +1620,12 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
await homeBridge
.fixAssetsAboveLimits(transactionHash, true, halfEther, { from: recipient })
.fixAssetsAboveLimits(messageId, true, halfEther, { from: recipient })
.should.be.rejectedWith(ERROR_MSG)
await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther, { from: owner }).should.be.fulfilled
await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther, { from: owner }).should.be.fulfilled
})
it('Should emit UserRequestForSignature with value reduced by fee', async () => {
// Initialize
@ -1671,13 +1680,14 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
value,
transactionHash
})
const messageId = affirmationLogs[0].args.messageId
const { logs } = await homeBridge.fixAssetsAboveLimits(transactionHash, true, halfEther).should.be.fulfilled
const { logs } = await homeBridge.fixAssetsAboveLimits(messageId, true, halfEther).should.be.fulfilled
// Then
logs.length.should.be.equal(2)
expectEventInLogs(logs, 'AssetAboveLimitsFixed', {
transactionHash,
messageId,
value: halfEther,
remaining: halfEther
})
@ -1686,6 +1696,61 @@ contract('HomeBridge_ERC20_to_Native', async accounts => {
value: finalValue
})
})
it('should not allow to fix assets after they were already processed', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.fulfilled
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(ZERO)
await homeBridge.fixAssetsAboveLimits(messageId, false, value).should.be.rejectedWith(ERROR_MSG)
})
it('should not allow to executeAffirmation after assets were fixed', async () => {
const recipient = accounts[5]
const value = oneEther
const transactionHash = '0x806335163828a8eda675cff9c84fa6e6c7cf06bb44cc6ec832e42fe789d01415'
const { logs: affirmationLogs } = await homeBridge.executeAffirmation(recipient, value, transactionHash, {
from: authorities[0]
}).should.be.fulfilled
affirmationLogs[0].event.should.be.equal('AmountLimitExceeded')
const messageId = affirmationLogs[0].args.messageId
const outOfLimitAmount = await homeBridge.outOfLimitAmount()
outOfLimitAmount.should.be.bignumber.equal(value)
await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
await homeBridge.setExecutionDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setExecutionMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.setDailyLimit(ether('2'), { from: owner }).should.be.fulfilled
await homeBridge.setMaxPerTx(value, { from: owner }).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.rejected
await homeBridge.fixAssetsAboveLimits(messageId, false, halfEther).should.be.fulfilled
await homeBridge.executeAffirmation(recipient, value, transactionHash, { from: authorities[0] }).should.be
.rejected
})
})
describe('#feeManager', async () => {
let homeBridge