diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..86ebbc484 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: release + +on: + release: + types: [created] + +jobs: + flats: + runs-on: ubuntu-latest + steps: + - id: get_release + uses: bruceadams/get-release@v1.2.1 + env: + GITHUB_TOKEN: ${{ github.token }} + - uses: actions/setup-node@v1 + with: + node-version: 10 + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + id: npm-cache + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} + - run: npm install + if: ${{ !steps.npm-cache.outputs.cache-hit }} + - run: npm run flatten + - run: zip flats $(find flats -name '*.sol') + - uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: flats.zip + asset_name: tokenbridge-contracts-flattened-${{ steps.get_release.outputs.tag_name }}.zip + asset_content_type: application/zip diff --git a/contracts/libraries/SafeERC20.sol b/contracts/libraries/SafeERC20.sol new file mode 100644 index 000000000..58adf3983 --- /dev/null +++ b/contracts/libraries/SafeERC20.sol @@ -0,0 +1,49 @@ +pragma solidity 0.4.24; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../interfaces/ERC677.sol"; + +/** + * @title SafeERC20 + * @dev Helper methods for safe token transfers. + * Functions perform additional checks to be sure that token transfer really happened. + */ +library SafeERC20 { + using SafeMath for uint256; + + /** + * @dev Same as ERC20.transfer(address,uint256) but with extra consistency checks. + * @param _token address of the token contract + * @param _to address of the receiver + * @param _value amount of tokens to send + */ + function safeTransfer(address _token, address _to, uint256 _value) internal { + LegacyERC20(_token).transfer(_to, _value); + assembly { + if returndatasize { + returndatacopy(0, 0, 32) + if iszero(mload(0)) { + revert(0, 0) + } + } + } + } + + /** + * @dev Same as ERC20.transferFrom(address,address,uint256) but with extra consistency checks. + * @param _token address of the token contract + * @param _from address of the sender + * @param _value amount of tokens to send + */ + function safeTransferFrom(address _token, address _from, uint256 _value) internal { + LegacyERC20(_token).transferFrom(_from, address(this), _value); + assembly { + if returndatasize { + returndatacopy(0, 0, 32) + if iszero(mload(0)) { + revert(0, 0) + } + } + } + } +} diff --git a/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol b/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol index b256d30e1..2554c5293 100644 --- a/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol +++ b/contracts/upgradeable_contracts/amb_erc20_to_native/BasicAMBErc20ToNative.sol @@ -51,7 +51,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, 0); + return (1, 1, 1); } /** diff --git a/contracts/upgradeable_contracts/amb_erc20_to_native/ForeignAMBErc20ToNative.sol b/contracts/upgradeable_contracts/amb_erc20_to_native/ForeignAMBErc20ToNative.sol index 152914f83..ee9290eec 100644 --- a/contracts/upgradeable_contracts/amb_erc20_to_native/ForeignAMBErc20ToNative.sol +++ b/contracts/upgradeable_contracts/amb_erc20_to_native/ForeignAMBErc20ToNative.sol @@ -3,6 +3,7 @@ pragma solidity 0.4.24; import "./BasicAMBErc20ToNative.sol"; import "../BaseERC677Bridge.sol"; import "../ReentrancyGuard.sol"; +import "../../libraries/SafeERC20.sol"; /** * @title ForeignAMBErc20ToNative @@ -10,6 +11,8 @@ import "../ReentrancyGuard.sol"; * It is design to be used as implementation contract of EternalStorageProxy contract. */ contract ForeignAMBErc20ToNative is BasicAMBErc20ToNative, ReentrancyGuard, BaseERC677Bridge { + using SafeERC20 for ERC677; + bytes32 internal constant MEDIATOR_BALANCE = 0x3db340e280667ee926fa8c51e8f9fcf88a0ff221a66d84d63b4778127d97d139; // keccak256(abi.encodePacked("mediatorBalance")) /** @@ -70,13 +73,12 @@ contract ForeignAMBErc20ToNative is BasicAMBErc20ToNative, ReentrancyGuard, Base // which will call passMessage. require(!lock()); ERC677 token = _erc677token(); - address to = address(this); require(withinLimit(_value)); addTotalSpentPerDay(getCurrentDay(), _value); _setMediatorBalance(mediatorBalance().add(_value)); setLock(true); - token.transferFrom(msg.sender, to, _value); + token.safeTransferFrom(msg.sender, _value); setLock(false); bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _value, abi.encodePacked(_receiver)); } @@ -150,7 +152,7 @@ contract ForeignAMBErc20ToNative is BasicAMBErc20ToNative, ReentrancyGuard, Base bytes32 _messageId = messageId(); _setMediatorBalance(mediatorBalance().sub(valueToTransfer)); - _erc677token().transfer(_receiver, valueToTransfer); + _erc677token().safeTransfer(_receiver, valueToTransfer); emit TokensBridged(_receiver, valueToTransfer, _messageId); } @@ -161,7 +163,7 @@ contract ForeignAMBErc20ToNative is BasicAMBErc20ToNative, ReentrancyGuard, Base */ function executeActionOnFixedTokens(address _receiver, uint256 _value) internal { _setMediatorBalance(mediatorBalance().sub(_value)); - _erc677token().transfer(_receiver, _value); + _erc677token().safeTransfer(_receiver, _value); } /** diff --git a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol index 9b657fc7d..3ddc69905 100644 --- a/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol +++ b/contracts/upgradeable_contracts/amb_erc677_to_erc677/BasicAMBErc677ToErc677.sol @@ -62,6 +62,12 @@ contract BasicAMBErc677ToErc677 is return mediatorContractOnOtherSide(); } + /** + * @dev Initiates the bridge operation that will lock the amount of tokens transferred and mint the tokens on + * the other network. The user should first call Approve method of the ERC677 token. + * @param _receiver address that will receive the minted tokens on the other network. + * @param _value amount of tokens to be transferred to the other network. + */ function relayTokens(address _receiver, uint256 _value) external { // This lock is to prevent calling passMessage twice if a ERC677 token is used. // When transferFrom is called, after the transfer, the ERC677 token will call onTokenTransfer from this contract @@ -90,7 +96,7 @@ contract BasicAMBErc677ToErc677 is } function getBridgeInterfacesVersion() external pure returns (uint64 major, uint64 minor, uint64 patch) { - return (1, 2, 0); + return (1, 2, 1); } function getBridgeMode() external pure returns (bytes4 _data) { diff --git a/contracts/upgradeable_contracts/amb_erc677_to_erc677/ForeignAMBErc677ToErc677.sol b/contracts/upgradeable_contracts/amb_erc677_to_erc677/ForeignAMBErc677ToErc677.sol index ac8e537cf..d65c29eb4 100644 --- a/contracts/upgradeable_contracts/amb_erc677_to_erc677/ForeignAMBErc677ToErc677.sol +++ b/contracts/upgradeable_contracts/amb_erc677_to_erc677/ForeignAMBErc677ToErc677.sol @@ -1,6 +1,7 @@ pragma solidity 0.4.24; import "./BasicAMBErc677ToErc677.sol"; +import "../../libraries/SafeERC20.sol"; /** * @title ForeignAMBErc677ToErc677 @@ -8,6 +9,8 @@ import "./BasicAMBErc677ToErc677.sol"; * It is designed to be used as an implementation contract of EternalStorageProxy contract. */ contract ForeignAMBErc677ToErc677 is BasicAMBErc677ToErc677 { + using SafeERC20 for ERC677; + /** * @dev Executes action on the request to withdraw tokens relayed from the other network * @param _recipient address of tokens receiver @@ -16,10 +19,31 @@ contract ForeignAMBErc677ToErc677 is BasicAMBErc677ToErc677 { function executeActionOnBridgedTokens(address _recipient, uint256 _value) internal { uint256 value = _unshiftValue(_value); bytes32 _messageId = messageId(); - erc677token().transfer(_recipient, value); + erc677token().safeTransfer(_recipient, value); emit TokensBridged(_recipient, value, _messageId); } + /** + * @dev Initiates the bridge operation that will lock the amount of tokens transferred and mint the tokens on + * the other network. The user should first call Approve method of the ERC677 token. + * @param _receiver address that will receive the minted tokens on the other network. + * @param _value amount of tokens to be transferred to the other network. + */ + function relayTokens(address _receiver, uint256 _value) external { + // This lock is to prevent calling passMessage twice if a ERC677 token is used. + // When transferFrom is called, after the transfer, the ERC677 token will call onTokenTransfer from this contract + // which will call passMessage. + require(!lock()); + ERC677 token = erc677token(); + require(withinLimit(_value)); + addTotalSpentPerDay(getCurrentDay(), _value); + + setLock(true); + token.safeTransferFrom(msg.sender, _value); + setLock(false); + bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _value, abi.encodePacked(_receiver)); + } + /** * @dev Executes action on deposit of bridged tokens * @param _from address of tokens sender @@ -38,6 +62,6 @@ contract ForeignAMBErc677ToErc677 is BasicAMBErc677ToErc677 { } function executeActionOnFixedTokens(address _recipient, uint256 _value) internal { - erc677token().transfer(_recipient, _value); + erc677token().safeTransfer(_recipient, _value); } } 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 7783e3afd..7c73dbf8c 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, 1, 0); + return (1, 1, 1); } /** 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 3e7830d21..71e37e89e 100644 --- a/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol +++ b/contracts/upgradeable_contracts/multi_amb_erc20_to_erc677/ForeignMultiAMBErc20ToErc677.sol @@ -4,6 +4,7 @@ import "openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol"; import "./BasicMultiAMBErc20ToErc677.sol"; import "./HomeMultiAMBErc20ToErc677.sol"; import "../../libraries/TokenReader.sol"; +import "../../libraries/SafeERC20.sol"; /** * @title ForeignMultiAMBErc20ToErc677 @@ -11,6 +12,9 @@ import "../../libraries/TokenReader.sol"; * It is designed to be used as an implementation contract of EternalStorageProxy contract. */ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { + using SafeERC20 for address; + using SafeERC20 for ERC677; + /** * @dev Stores the initial parameters of the mediator. * @param _bridgeContract the address of the AMB bridge contract. @@ -53,7 +57,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { */ function executeActionOnBridgedTokens(address _token, address _recipient, uint256 _value) internal { bytes32 _messageId = messageId(); - LegacyERC20(_token).transfer(_recipient, _value); + _token.safeTransfer(_recipient, _value); _setMediatorBalance(_token, mediatorBalance(_token).sub(_value)); emit TokensBridged(_token, _recipient, _value, _messageId); } @@ -97,10 +101,9 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { // When transferFrom is called, after the transfer, the ERC677 token will call onTokenTransfer from this contract // which will call passMessage. require(!lock()); - address to = address(this); setLock(true); - LegacyERC20(token).transferFrom(msg.sender, to, _value); + token.safeTransferFrom(msg.sender, _value); setLock(false); bridgeSpecificActionsOnTokenTransfer(token, msg.sender, _value, abi.encodePacked(_receiver)); } @@ -189,7 +192,7 @@ contract ForeignMultiAMBErc20ToErc677 is BasicMultiAMBErc20ToErc677 { */ function executeActionOnFixedTokens(address _token, address _recipient, uint256 _value) internal { _setMediatorBalance(_token, mediatorBalance(_token).sub(_value)); - LegacyERC20(_token).transfer(_recipient, _value); + _token.safeTransfer(_recipient, _value); } /** diff --git a/package-lock.json b/package-lock.json index 65f3f9c84..50d2644b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tokenbridge-contracts", - "version": "5.4.0", + "version": "5.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e7d5e34ee..3c0fd8520 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tokenbridge-contracts", - "version": "5.4.0", + "version": "5.4.1", "description": "Bridge", "main": "index.js", "scripts": {