diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2f72b1aad..8095c55fd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,9 +80,9 @@ jobs: steps: - uses: actions/checkout@v2 - if: startsWith(github.ref, 'refs/tags') - run: echo "::set-env name=DOCKER_TAGS::${GITHUB_REF#refs/tags/},latest" + run: echo "DOCKER_TAGS=${GITHUB_REF#refs/tags/},latest" >> $GITHUB_ENV - if: startsWith(github.ref, 'refs/heads') - run: echo "::set-env name=DOCKER_TAGS::${GITHUB_REF#refs/heads/}-${GITHUB_SHA::8}" + run: echo "DOCKER_TAGS=${GITHUB_REF#refs/heads/}-${GITHUB_SHA::8}" >> $GITHUB_ENV - uses: docker/build-push-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} diff --git a/contracts/libraries/ArbitraryMessage.sol b/contracts/libraries/ArbitraryMessage.sol index 846eb12bd..daf220f03 100644 --- a/contracts/libraries/ArbitraryMessage.sol +++ b/contracts/libraries/ArbitraryMessage.sol @@ -48,8 +48,8 @@ library ArbitraryMessage { gasLimit := and(shr(64, blob), 0xffffffff) dataType := byte(26, blob) - if gt(dataType, 0) { - // for now, only 0 datatype is supported - regular AMB calls + if gt(and(dataType, 0x7f), 0) { + // for now, only 0x00 and 0x80 datatypes are supported - regular AMB calls // other dataType values are kept reserved for future use revert(0, 0) } diff --git a/contracts/mocks/AMBMock.sol b/contracts/mocks/AMBMock.sol index 33ba903aa..6ba1215cf 100644 --- a/contracts/mocks/AMBMock.sol +++ b/contracts/mocks/AMBMock.sol @@ -46,7 +46,7 @@ contract AMBMock { } function requireToConfirmMessage(address _contract, bytes _data, uint256 _gas) external returns (bytes32) { - return _sendMessage(_contract, _data, _gas, 0xf0); + return _sendMessage(_contract, _data, _gas, 0x80); } function _sendMessage(address _contract, bytes _data, uint256 _gas, uint256 _dataType) internal returns (bytes32) { diff --git a/contracts/upgradeable_contracts/ReentrancyGuard.sol b/contracts/upgradeable_contracts/ReentrancyGuard.sol index 14c6874ac..a098981a0 100644 --- a/contracts/upgradeable_contracts/ReentrancyGuard.sol +++ b/contracts/upgradeable_contracts/ReentrancyGuard.sol @@ -1,15 +1,21 @@ pragma solidity 0.4.24; -import "../upgradeability/EternalStorage.sol"; - -contract ReentrancyGuard is EternalStorage { - bytes32 internal constant LOCK = 0x6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e92; // keccak256(abi.encodePacked("lock")) - - function lock() internal returns (bool) { - return boolStorage[LOCK]; +contract ReentrancyGuard { + function lock() internal returns (bool res) { + assembly { + // Even though this is not the same as boolStorage[keccak256(abi.encodePacked("lock"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + res := sload(0x6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e92) // keccak256(abi.encodePacked("lock")) + } } function setLock(bool _lock) internal { - boolStorage[LOCK] = _lock; + assembly { + // Even though this is not the same as boolStorage[keccak256(abi.encodePacked("lock"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + sstore(0x6168652c307c1e813ca11cfb3a601f1cf3b22452021a5052d8b05f1f1f8a3e92, _lock) // keccak256(abi.encodePacked("lock")) + } } } diff --git a/contracts/upgradeable_contracts/TokenBridgeMediator.sol b/contracts/upgradeable_contracts/TokenBridgeMediator.sol index 5cf9a972d..6d22cbe6a 100644 --- a/contracts/upgradeable_contracts/TokenBridgeMediator.sol +++ b/contracts/upgradeable_contracts/TokenBridgeMediator.sol @@ -10,6 +10,7 @@ import "./TransferInfoStorage.sol"; */ contract TokenBridgeMediator is BasicAMBMediator, BasicTokenBridge, TransferInfoStorage { event FailedMessageFixed(bytes32 indexed messageId, address recipient, uint256 value); + event TokensBridgingInitiated(address indexed sender, uint256 value, bytes32 indexed messageId); event TokensBridged(address indexed recipient, uint256 value, bytes32 indexed messageId); /** @@ -32,6 +33,8 @@ contract TokenBridgeMediator is BasicAMBMediator, BasicTokenBridge, TransferInfo setMessageValue(_messageId, _value); setMessageRecipient(_messageId, _from); + + emit TokensBridgingInitiated(_from, _value, _messageId); } /** diff --git a/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol b/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol index 58b435c86..9fc5ad2f7 100644 --- a/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol +++ b/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol @@ -50,7 +50,7 @@ contract BasicAMBErc20ToNative is Initializable, Upgradeable, Claimable, Version * @return patch value of the version */ function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 1, 1); + return (1, 2, 0); } /** diff --git a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol index 75e7c321d..0e5a20553 100644 --- a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol +++ b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol @@ -96,7 +96,7 @@ contract BasicAMBErc677ToErc677 is } function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 3, 0); + return (1, 4, 0); } function getBridgeMode() external pure returns (bytes4 _data) { diff --git a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicStakeTokenMediator.sol b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicStakeTokenMediator.sol index 90b816d3d..ccbbe5831 100644 --- a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicStakeTokenMediator.sol +++ b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicStakeTokenMediator.sol @@ -4,7 +4,7 @@ import "./BasicAMBErc677ToErc677.sol"; contract BasicStakeTokenMediator is BasicAMBErc677ToErc677 { function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 2, 0); + return (1, 3, 0); } function getBridgeMode() external pure returns (bytes4 _data) { diff --git a/contracts/upgradeable_contracts/amb_native_to_erc20/BasicAMBNativeToErc20.sol b/contracts/upgradeable_contracts/amb_native_to_erc20/BasicAMBNativeToErc20.sol index e7b3fc5d8..ea456ce88 100644 --- a/contracts/upgradeable_contracts/amb_native_to_erc20/BasicAMBNativeToErc20.sol +++ b/contracts/upgradeable_contracts/amb_native_to_erc20/BasicAMBNativeToErc20.sol @@ -61,7 +61,7 @@ contract BasicAMBNativeToErc20 is * @return patch value of the version */ function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 1, 0); + return (1, 2, 0); } /** diff --git a/contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol b/contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol index 15b481b65..fde2cbae3 100644 --- a/contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol +++ b/contracts/upgradeable_contracts/arbitrary_message/BasicHomeAMB.sol @@ -15,7 +15,7 @@ contract BasicHomeAMB is BasicAMB, MessageDelivery { uint256 NumberOfCollectedSignatures ); - uint256 internal constant SEND_TO_MANUAL_LANE = 0xf0; + uint256 internal constant SEND_TO_MANUAL_LANE = 0x80; function executeAffirmation(bytes message) external onlyValidator { bytes32 hashMsg = keccak256(abi.encodePacked(message)); diff --git a/contracts/upgradeable_contracts/arbitrary_message/MessageProcessor.sol b/contracts/upgradeable_contracts/arbitrary_message/MessageProcessor.sol index 34b196f5a..56aee03d9 100644 --- a/contracts/upgradeable_contracts/arbitrary_message/MessageProcessor.sol +++ b/contracts/upgradeable_contracts/arbitrary_message/MessageProcessor.sol @@ -4,10 +4,6 @@ import "../../upgradeability/EternalStorage.sol"; import "../../libraries/Bytes.sol"; contract MessageProcessor is EternalStorage { - bytes32 internal constant MESSAGE_SENDER = 0x7b58b2a669d8e0992eae9eaef641092c0f686fd31070e7236865557fa1571b5b; // keccak256(abi.encodePacked("messageSender")) - bytes32 internal constant MESSAGE_ID = 0xe34bb2103dc34f2c144cc216c132d6ffb55dac57575c22e089161bbe65083304; // keccak256(abi.encodePacked("messageId")) - bytes32 internal constant MESSAGE_SOURCE_CHAIN_ID = 0x7f0fcd9e49860f055dd0c1682d635d309ecb5e3011654c716d9eb59a7ddec7d2; // keccak256(abi.encodePacked("messageSourceChainId")) - /** * @dev Returns a status of the message that came from the other side. * @param _messageId id of the message from the other side that triggered a call. @@ -89,8 +85,13 @@ contract MessageProcessor is EternalStorage { * Can be used by executors for getting other side caller address. * @return address of the sender on the other side. */ - function messageSender() external view returns (address) { - return addressStorage[MESSAGE_SENDER]; + function messageSender() external view returns (address sender) { + assembly { + // Even though this is not the same as addressStorage[keccak256(abi.encodePacked("messageSender"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + sender := sload(0x7b58b2a669d8e0992eae9eaef641092c0f686fd31070e7236865557fa1571b5b) // keccak256(abi.encodePacked("messageSender")) + } } /** @@ -98,15 +99,25 @@ contract MessageProcessor is EternalStorage { * @param _sender address of the sender on the other side. */ function setMessageSender(address _sender) internal { - addressStorage[MESSAGE_SENDER] = _sender; + assembly { + // Even though this is not the same as addressStorage[keccak256(abi.encodePacked("messageSender"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + sstore(0x7b58b2a669d8e0992eae9eaef641092c0f686fd31070e7236865557fa1571b5b, _sender) // keccak256(abi.encodePacked("messageSender")) + } } /** * @dev Returns an id of the currently processed message. * @return id of the message that originated on the other side. */ - function messageId() public view returns (bytes32) { - return bytes32(uintStorage[MESSAGE_ID]); + function messageId() public view returns (bytes32 id) { + assembly { + // Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageId"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + id := sload(0xe34bb2103dc34f2c144cc216c132d6ffb55dac57575c22e089161bbe65083304) // keccak256(abi.encodePacked("messageId")) + } } /** @@ -124,23 +135,38 @@ contract MessageProcessor is EternalStorage { * @param _messageId id of the message that originated on the other side. */ function setMessageId(bytes32 _messageId) internal { - uintStorage[MESSAGE_ID] = uint256(_messageId); + assembly { + // Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageId"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + sstore(0xe34bb2103dc34f2c144cc216c132d6ffb55dac57575c22e089161bbe65083304, _messageId) // keccak256(abi.encodePacked("messageId")) + } } /** * @dev Returns an originating chain id of the currently processed message. * @return source chain id of the message that originated on the other side. */ - function messageSourceChainId() external view returns (uint256) { - return uintStorage[MESSAGE_SOURCE_CHAIN_ID]; + function messageSourceChainId() external view returns (uint256 id) { + assembly { + // Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageSourceChainId"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + id := sload(0x7f0fcd9e49860f055dd0c1682d635d309ecb5e3011654c716d9eb59a7ddec7d2) // keccak256(abi.encodePacked("messageSourceChainId")) + } } /** - * @dev Returns an originating chain id of the currently processed message. - * @return source chain id of the message that originated on the other side. + * @dev Sets an originating chain id of the currently processed message. + * @param _sourceChainId source chain id of the message that originated on the other side. */ - function setMessageSourceChainId(uint256 _sourceChainId) internal returns (uint256) { - uintStorage[MESSAGE_SOURCE_CHAIN_ID] = _sourceChainId; + function setMessageSourceChainId(uint256 _sourceChainId) internal { + assembly { + // Even though this is not the same as uintStorage[keccak256(abi.encodePacked("messageSourceChainId"))], + // since solidity mapping introduces another level of addressing, such slot change is safe + // for temporary variables which are cleared at the end of the call execution. + sstore(0x7f0fcd9e49860f055dd0c1682d635d309ecb5e3011654c716d9eb59a7ddec7d2, _sourceChainId) // keccak256(abi.encodePacked("messageSourceChainId")) + } } /** @@ -195,7 +221,7 @@ contract MessageProcessor is EternalStorage { setMessageSourceChainId(_sourceChainId); // After EIP-150, max gas cost allowed to be passed to the internal call is equal to the 63/64 of total gas left. - // In reallity, min(gasLimit, 63/64 * gasleft()) will be used as the call gas limit. + // In reality, min(gasLimit, 63/64 * gasleft()) will be used as the call gas limit. // Imagine a situation, when message requires 10000000 gas to be executed successfully. // Also suppose, that at this point, gasleft() is equal to 10158000, so the callee will receive ~ 10158000 * 63 / 64 = 9999300 gas. // That amount of gas is not enough, so the call will fail. At the same time, @@ -203,7 +229,7 @@ contract MessageProcessor is EternalStorage { // finish its execution and it will be enough. The internal call fails but // only because the oracle provides incorrect gas limit for the transaction // This check is needed here in order to force contract to pass exactly the requested amount of gas. - // Avoiding it may leed to the unwanted message failure in some extreme cases. + // Avoiding it may lead to the unwanted message failure in some extreme cases. require((gasleft() * 63) / 64 > _gas); bool status = _contract.call.gas(_gas)(_data); diff --git a/contracts/upgradeable_contracts/arbitrary_message/VersionableAMB.sol b/contracts/upgradeable_contracts/arbitrary_message/VersionableAMB.sol index 0e692dcb1..573b882d9 100644 --- a/contracts/upgradeable_contracts/arbitrary_message/VersionableAMB.sol +++ b/contracts/upgradeable_contracts/arbitrary_message/VersionableAMB.sol @@ -17,6 +17,6 @@ contract VersionableAMB is VersionableBridge { * @return (major, minor, patch) version triple */ function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (5, 4, 0); + return (5, 5, 0); } } diff --git a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/BasicMultiAMBErc20ToErc677.sol b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/BasicMultiAMBErc20ToErc677.sol index 1af3cdbfc..0fca6b578 100644 --- a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/BasicMultiAMBErc20ToErc677.sol +++ b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/BasicMultiAMBErc20ToErc677.sol @@ -58,7 +58,7 @@ contract BasicMultiAMBErc20ToErc677 is * @return patch value of the version */ function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 2, 0); + return (1, 3, 0); } /** @@ -88,5 +88,6 @@ contract BasicMultiAMBErc20ToErc677 is function _relayTokens(ERC677 token, address _receiver, uint256 _value) internal; /* solcov ignore next */ - function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal; + function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, address _receiver, uint256 _value) + internal; } diff --git a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol index ec179c728..962a37c32 100644 --- a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol +++ b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol @@ -70,7 +70,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { function onTokenTransfer(address _from, uint256 _value, bytes _data) public returns (bool) { if (!lock()) { ERC677 token = ERC677(msg.sender); - bridgeSpecificActionsOnTokenTransfer(token, _from, _value, _data); + bridgeSpecificActionsOnTokenTransfer(token, _from, chooseReceiver(_from, _data), _value); } return true; } @@ -104,19 +104,19 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { setLock(true); token.safeTransferFrom(msg.sender, _value); setLock(false); - bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _value, abi.encodePacked(_receiver)); + bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _receiver, _value); } /** * @dev Executes action on deposit of bridged tokens * @param _token address of the token contract * @param _from address of tokens sender - * @param _value requsted amount of bridged tokens - * @param _data alternative receiver, if specified + * @param _receiver address of tokens receiver on the other side + * @param _value requested amount of bridged tokens */ - function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal { - if (lock()) return; - + function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, address _receiver, uint256 _value) + internal + { bool isKnownToken = isTokenRegistered(_token); if (!isKnownToken) { string memory name = TokenReader.readName(_token); @@ -132,10 +132,9 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { addTotalSpentPerDay(_token, getCurrentDay(), _value); bytes memory data; - address receiver = chooseReceiver(_from, _data); if (isKnownToken) { - data = abi.encodeWithSelector(this.handleBridgedTokens.selector, _token, receiver, _value); + data = abi.encodeWithSelector(this.handleBridgedTokens.selector, _token, _receiver, _value); } else { data = abi.encodeWithSelector( HomeMultiAMBErc20ToErc677(this).deployAndHandleBridgedTokens.selector, @@ -143,7 +142,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { name, symbol, decimals, - receiver, + _receiver, _value ); } @@ -163,6 +162,8 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { if (!isKnownToken) { _setTokenRegistrationMessageId(_token, _messageId); } + + emit TokensBridgingInitiated(_token, _from, _value, _messageId); } /** diff --git a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/HomeMultiAMBErc20ToErc677.sol b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/HomeMultiAMBErc20ToErc677.sol index d64ab1a08..e2d579bc9 100644 --- a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/HomeMultiAMBErc20ToErc677.sol +++ b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/HomeMultiAMBErc20ToErc677.sol @@ -148,7 +148,7 @@ contract HomeMultiAMBErc20ToErc677 is // require(isTokenRegistered(token)); require(withinLimit(token, _value)); addTotalSpentPerDay(token, getCurrentDay(), _value); - bridgeSpecificActionsOnTokenTransfer(token, _from, _value, _data); + bridgeSpecificActionsOnTokenTransfer(token, _from, chooseReceiver(_from, _data), _value); } return true; } @@ -176,7 +176,7 @@ contract HomeMultiAMBErc20ToErc677 is setLock(true); token.transferFrom(msg.sender, to, _value); setLock(false); - bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _value, abi.encodePacked(_receiver)); + bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _receiver, _value); } /** @@ -247,26 +247,26 @@ contract HomeMultiAMBErc20ToErc677 is * @dev Executes action on withdrawal of bridged tokens * @param _token address of token contract * @param _from address of tokens sender + * @param _receiver address of tokens receiver on the other side * @param _value requested amount of bridged tokens - * @param _data alternative receiver, if specified */ - function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, uint256 _value, bytes _data) internal { - if (!lock()) { - uint256 valueToBridge = _value; - uint256 fee = 0; - // Next line disables fee collection in case sender is one of the reward addresses. - // It is needed to allow a 100% withdrawal of tokens from the home side. - // If fees are not disabled for reward receivers, small fraction of tokens will always - // be redistributed between the same set of reward addresses, which is not the desired behaviour. - if (!isRewardAddress(_from)) { - fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToBridge); - valueToBridge = valueToBridge.sub(fee); - } - IBurnableMintableERC677Token(_token).burn(valueToBridge); - bytes32 _messageId = passMessage(_token, _from, chooseReceiver(_from, _data), valueToBridge); - if (fee > 0) { - emit FeeDistributed(fee, _token, _messageId); - } + function bridgeSpecificActionsOnTokenTransfer(ERC677 _token, address _from, address _receiver, uint256 _value) + internal + { + uint256 valueToBridge = _value; + uint256 fee = 0; + // Next line disables fee collection in case sender is one of the reward addresses. + // It is needed to allow a 100% withdrawal of tokens from the home side. + // If fees are not disabled for reward receivers, small fraction of tokens will always + // be redistributed between the same set of reward addresses, which is not the desired behaviour. + if (!isRewardAddress(_from)) { + fee = _distributeFee(HOME_TO_FOREIGN_FEE, _token, valueToBridge); + valueToBridge = valueToBridge.sub(fee); + } + IBurnableMintableERC677Token(_token).burn(valueToBridge); + bytes32 _messageId = passMessage(_token, _from, _receiver, valueToBridge); + if (fee > 0) { + emit FeeDistributed(fee, _token, _messageId); } } @@ -299,6 +299,8 @@ contract HomeMultiAMBErc20ToErc677 is setMessageValue(_messageId, _value); setMessageRecipient(_messageId, _from); + emit TokensBridgingInitiated(_token, _from, _value, _messageId); + return _messageId; } } diff --git a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/MultiTokenBridgeMediator.sol b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/MultiTokenBridgeMediator.sol index d41939b0a..ef2fbc3b4 100644 --- a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/MultiTokenBridgeMediator.sol +++ b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/MultiTokenBridgeMediator.sol @@ -17,6 +17,12 @@ contract MultiTokenBridgeMediator is ChooseReceiverHelper { event FailedMessageFixed(bytes32 indexed messageId, address token, address recipient, uint256 value); + event TokensBridgingInitiated( + address indexed token, + address indexed sender, + uint256 value, + bytes32 indexed messageId + ); event TokensBridged(address indexed token, address indexed recipient, uint256 value, bytes32 indexed messageId); /** diff --git a/deploy/deploy.js b/deploy/deploy.js index ac0193460..1462f1282 100644 --- a/deploy/deploy.js +++ b/deploy/deploy.js @@ -77,9 +77,10 @@ async function deployErcToNative() { } async function deployArbitraryMessage() { + const preDeploy = require('./src/arbitrary_message/preDeploy') const deployHome = require('./src/arbitrary_message/home') const deployForeign = require('./src/arbitrary_message/foreign') - + await preDeploy() const { homeBridge } = await deployHome() const { foreignBridge } = await deployForeign() console.log('\nDeployment has been completed.\n\n') diff --git a/deploy/src/arbitrary_message/preDeploy.js b/deploy/src/arbitrary_message/preDeploy.js new file mode 100644 index 000000000..906c2f003 --- /dev/null +++ b/deploy/src/arbitrary_message/preDeploy.js @@ -0,0 +1,14 @@ +const { web3Home, web3Foreign } = require('../web3') + +async function preDeploy() { + const homeChainId = await web3Home.eth.getChainId() + const foreignChainId = await web3Foreign.eth.getChainId() + + if (homeChainId === foreignChainId) { + throw new Error( + `Chain ids on Home and Foreign networks should be different. Got the same value of ${homeChainId} on both networks instead.` + ) + } +} + +module.exports = preDeploy diff --git a/package-lock.json b/package-lock.json index ebce43663..150acffde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tokenbridge-contracts", - "version": "5.5.0", + "version": "5.6.0-rc0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 833576755..a47f0fd6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tokenbridge-contracts", - "version": "5.5.0", + "version": "5.6.0-rc0", "description": "Bridge", "main": "index.js", "scripts": { diff --git a/test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js b/test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js index 1b9e9318e..cb294f9d3 100644 --- a/test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js +++ b/test/amb_erc677_to_erc677/AMBErc677ToErc677Behavior.test.js @@ -429,7 +429,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou it('Should allow to partially reduce outOfLimitAmount and emit amb event', async function() { const { logs } = await contract.fixAssetsAboveLimits(exampleMessageId, true, halfEther).should.be.fulfilled - logs.length.should.be.equal(1) + logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: halfEther, @@ -442,7 +442,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou const { logs: logsSecondTx } = await contract.fixAssetsAboveLimits(exampleMessageId, true, halfEther).should.be .fulfilled - logsSecondTx.length.should.be.equal(1) + logsSecondTx.length.should.be.equal(2) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: halfEther, @@ -455,7 +455,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou it('Should revert if try to unlock more than available', async function() { const { logs } = await contract.fixAssetsAboveLimits(exampleMessageId, true, halfEther).should.be.fulfilled - logs.length.should.be.equal(1) + logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: halfEther, @@ -465,7 +465,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou const { logs: logsSecondTx } = await contract.fixAssetsAboveLimits(exampleMessageId, true, halfEther).should.be .fulfilled - logsSecondTx.length.should.be.equal(1) + logsSecondTx.length.should.be.equal(2) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: halfEther, @@ -475,7 +475,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou const { logs: logsThirdTx } = await contract.fixAssetsAboveLimits(exampleMessageId, true, halfEther).should.be .fulfilled - logsThirdTx.length.should.be.equal(1) + logsThirdTx.length.should.be.equal(2) expectEventInLogs(logsThirdTx, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: halfEther, @@ -487,7 +487,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou it('Should not be allow to be called by an already fixed message', async function() { const { logs } = await contract.fixAssetsAboveLimits(exampleMessageId, true, oneEther).should.be.fulfilled - logs.length.should.be.equal(1) + logs.length.should.be.equal(2) expectEventInLogs(logs, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: oneEther, @@ -497,7 +497,7 @@ function shouldBehaveLikeBasicAMBErc677ToErc677(otherSideMediatorContract, accou const { logs: logsSecondTx } = await contract.fixAssetsAboveLimits(exampleMessageId, true, oneEther).should.be .fulfilled - logsSecondTx.length.should.be.equal(1) + logsSecondTx.length.should.be.equal(2) expectEventInLogs(logsSecondTx, 'AssetAboveLimitsFixed', { messageId: exampleMessageId, value: oneEther, diff --git a/test/arbitrary_message/home_bridge.test.js b/test/arbitrary_message/home_bridge.test.js index 92923cbff..7617b5942 100644 --- a/test/arbitrary_message/home_bridge.test.js +++ b/test/arbitrary_message/home_bridge.test.js @@ -304,7 +304,7 @@ contract('HomeAMB', async accounts => { tx.receipt.logs.length.should.be.equal(1) const { messageId, encodedData } = tx.receipt.logs[0].args expect(messageId).to.include(`${bridgeId}0000000000000000`) - expect(encodedData.substr(2 + (32 + 20 + 20 + 4 + 1 + 1) * 2, 2)).to.be.equal('f0') + expect(encodedData.substr(2 + (32 + 20 + 20 + 4 + 1 + 1) * 2, 2)).to.be.equal('80') }) it('call requireToPassMessage(address, bytes, uint256) should fail', async () => { // Should fail because gas < minimumGasUsage diff --git a/test/multi_amb_erc20_to_erc677/foreign_mediator.test.js b/test/multi_amb_erc20_to_erc677/foreign_mediator.test.js index 67109e612..4ce0a0602 100644 --- a/test/multi_amb_erc20_to_erc677/foreign_mediator.test.js +++ b/test/multi_amb_erc20_to_erc677/foreign_mediator.test.js @@ -367,6 +367,13 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => { expect(await contract.mediatorBalance(token.address)).to.be.bignumber.equal(halfEther) expect(await contract.isTokenRegistered(token.address)).to.be.equal(true) expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(halfEther) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(token.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(halfEther.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should respect global shutdown', async () => { @@ -392,6 +399,13 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => { expect(await contract.mediatorBalance(token.address)).to.be.bignumber.equal(halfEther) expect(await contract.isTokenRegistered(token.address)).to.be.equal(true) expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(halfEther) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(token.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(halfEther.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) }) @@ -416,6 +430,13 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value) expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(value) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(token.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(value.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should allow to specify a different receiver without specifying sender', async () => { @@ -433,6 +454,13 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user2).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value) expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(value) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(token.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(value.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should allow to specify no receiver and no sender', async () => { @@ -449,6 +477,13 @@ contract('ForeignMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(token.address, currentDay)).to.be.bignumber.equal(value) expect(await token.balanceOf(contract.address)).to.be.bignumber.equal(value) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(token.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(value.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should fail if user did not approve the transfer', async () => { diff --git a/test/multi_amb_erc20_to_erc677/home_mediator.test.js b/test/multi_amb_erc20_to_erc677/home_mediator.test.js index 84ebfdab0..59de0ea45 100644 --- a/test/multi_amb_erc20_to_erc677/home_mediator.test.js +++ b/test/multi_amb_erc20_to_erc677/home_mediator.test.js @@ -607,6 +607,13 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(halfEther) expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(homeToken.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(halfEther.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should respect global shutdown', async () => { @@ -630,6 +637,13 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user2).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(halfEther) expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(homeToken.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(halfEther.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) }) @@ -656,6 +670,13 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(value) expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(homeToken.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(value.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should allow to specify a different receiver without specifying sender', async () => { @@ -674,6 +695,13 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => { expect(events[0].returnValues.encodedData.includes(strip0x(user2).toLowerCase())).to.be.equal(true) expect(await contract.totalSpentPerDay(homeToken.address, currentDay)).to.be.bignumber.equal(value) expect(await homeToken.balanceOf(contract.address)).to.be.bignumber.equal(ZERO) + + const depositEvents = await getEvents(contract, { event: 'TokensBridgingInitiated' }) + expect(depositEvents.length).to.be.equal(1) + expect(depositEvents[0].returnValues.token).to.be.equal(homeToken.address) + expect(depositEvents[0].returnValues.sender).to.be.equal(user) + expect(depositEvents[0].returnValues.value).to.be.equal(value.toString()) + expect(depositEvents[0].returnValues.messageId).to.include('0x11223344') }) it('should allow to specify no receiver and no sender', async () => { @@ -1048,7 +1076,7 @@ contract('HomeMultiAMBErc20ToErc677', async accounts => { const events = await getEvents(ambBridgeContract, { event: 'MockedEvent' }) expect(events.length).to.be.equal(2) expect(strip0x(events[0].returnValues.encodedData).slice(156, 158)).to.be.equal('00') - expect(strip0x(events[1].returnValues.encodedData).slice(156, 158)).to.be.equal('f0') + expect(strip0x(events[1].returnValues.encodedData).slice(156, 158)).to.be.equal('80') }) }) })