diff options
Diffstat (limited to 'contracts')
107 files changed, 1711 insertions, 282 deletions
diff --git a/contracts/.solhint.json b/contracts/.solhint.json index 076afe9f3..3090d981a 100644 --- a/contracts/.solhint.json +++ b/contracts/.solhint.json @@ -5,6 +5,7 @@ "avoid-tx-origin": "warn", "bracket-align": false, "code-complexity": false, + "compiler-fixed": false, "const-name-snakecase": "error", "expression-indent": "error", "function-max-lines": false, diff --git a/contracts/examples/package.json b/contracts/examples/package.json index 77846241e..37b73f98c 100644 --- a/contracts/examples/package.json +++ b/contracts/examples/package.json @@ -13,7 +13,8 @@ "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s compile generate_contract_wrappers", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index da4d9c2ba..40e25565e 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -1,10 +1,27 @@ [ { + "version": "1.2.0", + "changes": [ + { + "note": "Added Dutch Auction Wrapper", + "pr": 1465 + } + ] + }, + { "version": "1.1.0", "changes": [ { "note": "Added Balance Threshold Filter", "pr": 1383 + }, + { + "note": "Add OrderMatcher", + "pr": 1117 + }, + { + "note": "Add OrderValidator", + "pr": 1464 } ] }, diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index e6ed0c215..2bb468724 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -18,5 +18,5 @@ } } }, - "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"] + "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder", "OrderMatcher", "OrderValidator"] } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index df830f36e..da050bf72 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-libs/contracts/libs/LibExchangeSelectors.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol index bd26a468f..a15e95a9d 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol @@ -17,7 +17,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./mixins/MExchangeCalldata.sol"; import "@0x/contracts-libs/contracts/libs/LibAddressArray.sol"; diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol index 3d8e2bbd1..4a1bf1fb2 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol @@ -17,7 +17,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IBalanceThresholdFilterCore { diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol index 3e424b9f4..f78f9c2de 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IThresholdAsset { diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol index b8b67e6ee..074686e8d 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "../interfaces/IThresholdAsset.sol"; diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol index bf2940fe1..40536d820 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol @@ -17,7 +17,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract MExchangeCalldata { diff --git a/contracts/extensions/contracts/Forwarder/MixinAssets.sol b/contracts/extensions/contracts/Forwarder/MixinAssets.sol index 3ebf75161..116cdf267 100644 --- a/contracts/extensions/contracts/Forwarder/MixinAssets.sol +++ b/contracts/extensions/contracts/Forwarder/MixinAssets.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; diff --git a/contracts/extensions/contracts/Forwarder/MixinExchangeWrapper.sol b/contracts/extensions/contracts/Forwarder/MixinExchangeWrapper.sol index 210eb14c2..cab26741d 100644 --- a/contracts/extensions/contracts/Forwarder/MixinExchangeWrapper.sol +++ b/contracts/extensions/contracts/Forwarder/MixinExchangeWrapper.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "./libs/LibConstants.sol"; diff --git a/contracts/extensions/contracts/Forwarder/MixinForwarderCore.sol b/contracts/extensions/contracts/Forwarder/MixinForwarderCore.sol index bab78d79b..11c0147a5 100644 --- a/contracts/extensions/contracts/Forwarder/MixinForwarderCore.sol +++ b/contracts/extensions/contracts/Forwarder/MixinForwarderCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "./libs/LibConstants.sol"; diff --git a/contracts/extensions/contracts/Forwarder/MixinWeth.sol b/contracts/extensions/contracts/Forwarder/MixinWeth.sol index 2a281f3ae..25a35f47b 100644 --- a/contracts/extensions/contracts/Forwarder/MixinWeth.sol +++ b/contracts/extensions/contracts/Forwarder/MixinWeth.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-libs/contracts/libs/LibMath.sol"; import "./libs/LibConstants.sol"; diff --git a/contracts/extensions/contracts/Forwarder/interfaces/IAssets.sol b/contracts/extensions/contracts/Forwarder/interfaces/IAssets.sol index 1e034c003..cebfd3706 100644 --- a/contracts/extensions/contracts/Forwarder/interfaces/IAssets.sol +++ b/contracts/extensions/contracts/Forwarder/interfaces/IAssets.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IAssets { diff --git a/contracts/extensions/contracts/Forwarder/interfaces/IForwarder.sol b/contracts/extensions/contracts/Forwarder/interfaces/IForwarder.sol index f5a26e2ba..6ce8a1d31 100644 --- a/contracts/extensions/contracts/Forwarder/interfaces/IForwarder.sol +++ b/contracts/extensions/contracts/Forwarder/interfaces/IForwarder.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "./IForwarderCore.sol"; diff --git a/contracts/extensions/contracts/Forwarder/interfaces/IForwarderCore.sol b/contracts/extensions/contracts/Forwarder/interfaces/IForwarderCore.sol index eede20bb8..7f62722e7 100644 --- a/contracts/extensions/contracts/Forwarder/interfaces/IForwarderCore.sol +++ b/contracts/extensions/contracts/Forwarder/interfaces/IForwarderCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/extensions/contracts/Forwarder/libs/LibConstants.sol b/contracts/extensions/contracts/Forwarder/libs/LibConstants.sol index 4a81abf76..0d2f6e36d 100644 --- a/contracts/extensions/contracts/Forwarder/libs/LibConstants.sol +++ b/contracts/extensions/contracts/Forwarder/libs/LibConstants.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; diff --git a/contracts/extensions/contracts/Forwarder/libs/LibForwarderErrors.sol b/contracts/extensions/contracts/Forwarder/libs/LibForwarderErrors.sol index fb3ade1db..7a95b78a0 100644 --- a/contracts/extensions/contracts/Forwarder/libs/LibForwarderErrors.sol +++ b/contracts/extensions/contracts/Forwarder/libs/LibForwarderErrors.sol @@ -17,7 +17,7 @@ */ // solhint-disable -pragma solidity 0.4.24; +pragma solidity ^0.4.24; /// This contract is intended to serve as a reference, but is not actually used for efficiency reasons. diff --git a/contracts/extensions/contracts/Forwarder/mixins/MAssets.sol b/contracts/extensions/contracts/Forwarder/mixins/MAssets.sol index 9e7f80d97..1757b37fb 100644 --- a/contracts/extensions/contracts/Forwarder/mixins/MAssets.sol +++ b/contracts/extensions/contracts/Forwarder/mixins/MAssets.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "../interfaces/IAssets.sol"; diff --git a/contracts/extensions/contracts/Forwarder/mixins/MExchangeWrapper.sol b/contracts/extensions/contracts/Forwarder/mixins/MExchangeWrapper.sol index d9e71786a..143f888b1 100644 --- a/contracts/extensions/contracts/Forwarder/mixins/MExchangeWrapper.sol +++ b/contracts/extensions/contracts/Forwarder/mixins/MExchangeWrapper.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/extensions/contracts/Forwarder/mixins/MWeth.sol b/contracts/extensions/contracts/Forwarder/mixins/MWeth.sol index 88e77be4e..15d66942f 100644 --- a/contracts/extensions/contracts/Forwarder/mixins/MWeth.sol +++ b/contracts/extensions/contracts/Forwarder/mixins/MWeth.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract MWeth { diff --git a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol new file mode 100644 index 000000000..f0f91cfc0 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol @@ -0,0 +1,195 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol"; +import "./mixins/MAssets.sol"; +import "./libs/LibConstants.sol"; + + +contract MixinAssets is + MAssets, + Ownable, + LibConstants +{ + using LibBytes for bytes; + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + transferAssetToSender(assetData, amount); + } + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + approveERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + approveERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + transferERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + transferERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + // 4 byte id + 12 0 bytes before ABI encoded token address. + address token = assetData.readAddress(16); + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + bool success = token.call(abi.encodeWithSelector( + ERC20_TRANSFER_SELECTOR, + msg.sender, + amount + )); + require( + success, + "TRANSFER_FAILED" + ); + + // Check return data. + // If there is no return data, we assume the token incorrectly + // does not return a bool. In this case we expect it to revert + // on failure, which was handled above. + // If the token does return data, we require that it is a single + // value that evaluates to true. + assembly { + if returndatasize { + success := 0 + if eq(returndatasize, 32) { + // First 64 bytes of memory are reserved scratch space + returndatacopy(0, 0, 32) + success := mload(0) + } + } + } + require( + success, + "TRANSFER_FAILED" + ); + } + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + require( + amount == 1, + "INVALID_AMOUNT" + ); + // Decode asset data. + // 4 byte id + 12 0 bytes before ABI encoded token address. + address token = assetData.readAddress(16); + // 4 byte id + 32 byte ABI encoded token address before token id. + uint256 tokenId = assetData.readUint256(36); + + // Perform transfer. + IERC721Token(token).transferFrom( + address(this), + msg.sender, + tokenId + ); + } + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + require( + IERC20Token(token).approve(ERC20_PROXY_ADDRESS, amount), + "APPROVAL_FAILED" + ); + } + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + bool approval = amount >= 1; + IERC721Token(token).setApprovalForAll(ERC721_PROXY_ADDRESS, approval); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol new file mode 100644 index 000000000..1787deb59 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -0,0 +1,86 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; +pragma experimental ABIEncoderV2; + +import "./libs/LibConstants.sol"; +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; + + +contract MixinMatchOrders is + Ownable, + LibConstants +{ + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + onlyOwner + { + // Match orders, maximally filling `leftOrder` + LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( + leftOrder, + rightOrder, + leftSignature, + rightSignature + ); + + uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; + uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount; + + // Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled + if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; + } + + // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. + rightOrder.makerAssetData = leftOrder.takerAssetData; + rightOrder.takerAssetData = leftOrder.makerAssetData; + + // Query `rightOrder` info to check if it has been completely filled + // We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call + LibOrder.OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder); + + // Do not attempt to call `fillOrder` if order has been completely filled + if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; + } + + // We do not need to pass in a signature since it was already validated in the `matchOrders` call + EXCHANGE.fillOrder( + rightOrder, + leftMakerAssetSpreadAmount, + "" + ); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol new file mode 100644 index 000000000..4879b7bca --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol @@ -0,0 +1,38 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; +import "./libs/LibConstants.sol"; +import "./MixinMatchOrders.sol"; +import "./MixinAssets.sol"; + + +// solhint-disable no-empty-blocks +contract OrderMatcher is + MixinMatchOrders, + MixinAssets +{ + constructor (address _exchange) + public + LibConstants(_exchange) + Ownable() + {} +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol new file mode 100644 index 000000000..9fcc0023a --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol @@ -0,0 +1,43 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + + +contract IAssets { + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external; + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol new file mode 100644 index 000000000..1443c73b9 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol @@ -0,0 +1,43 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; + + +contract IMatchOrders { + + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol new file mode 100644 index 000000000..75f26dca6 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol @@ -0,0 +1,31 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +import "@0x/contract-utils/contracts/utils/Ownable/IOwnable.sol"; +import "./IMatchOrders.sol"; +import "./IAssets.sol"; + + +// solhint-disable no-empty-blocks +contract IOrderMatcher is + IOwnable, + IMatchOrders, + IAssets +{} diff --git a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol new file mode 100644 index 000000000..c1a86a9c7 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol @@ -0,0 +1,56 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; + + +contract LibConstants { + + // bytes4(keccak256("transfer(address,uint256)")) + bytes4 constant internal ERC20_TRANSFER_SELECTOR = 0xa9059cbb; + // bytes4(keccak256("ERC20Token(address)")) + bytes4 constant internal ERC20_DATA_ID = 0xf47261b0; + // bytes4(keccak256("ERC721Token(address,uint256)")) + bytes4 constant internal ERC721_DATA_ID = 0x02571792; + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + address internal ERC20_PROXY_ADDRESS; + address internal ERC721_PROXY_ADDRESS; + // solhint-enable var-name-mixedcase + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + + ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_DATA_ID); + require( + ERC20_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + + ERC721_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC721_DATA_ID); + require( + ERC721_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol new file mode 100644 index 000000000..6ea7a3290 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol @@ -0,0 +1,71 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.24; + +import "../interfaces/IAssets.sol"; + + +contract MAssets is + IAssets +{ + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; +} diff --git a/contracts/protocol/contracts/protocol/OrderValidator/OrderValidator.sol b/contracts/extensions/contracts/OrderValidator/OrderValidator.sol index 33dd1326c..33dd1326c 100644 --- a/contracts/protocol/contracts/protocol/OrderValidator/OrderValidator.sol +++ b/contracts/extensions/contracts/OrderValidator/OrderValidator.sol diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index 2d9ed4dcd..aee995645 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", @@ -31,7 +32,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder).json" + "abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder|OrderMatcher|OrderValidator).json" }, "repository": { "type": "git", @@ -44,6 +45,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "devDependencies": { "@0x/abi-gen": "^1.0.19", + "@0x/contract-wrappers": "^4.1.3", "@0x/contracts-test-utils": "^1.0.2", "@0x/dev-utils": "^1.0.21", "@0x/sol-compiler": "^1.1.16", diff --git a/contracts/extensions/src/artifacts/index.ts b/contracts/extensions/src/artifacts/index.ts index ebf0b8050..329d0f3f3 100644 --- a/contracts/extensions/src/artifacts/index.ts +++ b/contracts/extensions/src/artifacts/index.ts @@ -3,9 +3,13 @@ import { ContractArtifact } from 'ethereum-types'; import * as BalanceThresholdFilter from '../../generated-artifacts/BalanceThresholdFilter.json'; import * as DutchAuction from '../../generated-artifacts/DutchAuction.json'; import * as Forwarder from '../../generated-artifacts/Forwarder.json'; +import * as OrderMatcher from '../../generated-artifacts/OrderMatcher.json'; +import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; export const artifacts = { BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact, DutchAuction: DutchAuction as ContractArtifact, Forwarder: Forwarder as ContractArtifact, + OrderMatcher: OrderMatcher as ContractArtifact, + OrderValidator: OrderValidator as ContractArtifact, }; diff --git a/contracts/extensions/src/wrappers/index.ts b/contracts/extensions/src/wrappers/index.ts index 8a8122caa..65aec3ccd 100644 --- a/contracts/extensions/src/wrappers/index.ts +++ b/contracts/extensions/src/wrappers/index.ts @@ -1,3 +1,5 @@ export * from '../../generated-wrappers/balance_threshold_filter'; export * from '../../generated-wrappers/dutch_auction'; export * from '../../generated-wrappers/forwarder'; +export * from '../../generated-wrappers/order_matcher'; +export * from '../../generated-wrappers/order_validator'; diff --git a/contracts/extensions/test/extensions/dutch_auction.ts b/contracts/extensions/test/extensions/dutch_auction.ts index 6c3b2f0f3..22b3caa16 100644 --- a/contracts/extensions/test/extensions/dutch_auction.ts +++ b/contracts/extensions/test/extensions/dutch_auction.ts @@ -1,3 +1,4 @@ +import { DutchAuctionWrapper } from '@0x/contract-wrappers'; import { artifacts as protocolArtifacts, ERC20Wrapper, @@ -29,12 +30,11 @@ import { RevertReason, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; -import ethAbi = require('ethereumjs-abi'); -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; import { artifacts } from '../../src/artifacts'; +import { DutchAuctionTestWrapper } from '../utils/dutch_auction_test_wrapper'; chaiSetup.configure(); const expect = chai.expect; @@ -68,19 +68,8 @@ describe(ContractName.DutchAuction, () => { let erc721MakerAssetIds: BigNumber[]; const tenMinutesInSeconds = 10 * 60; - function extendMakerAssetData(makerAssetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string { - return ethUtil.bufferToHex( - Buffer.concat([ - ethUtil.toBuffer(makerAssetData), - ethUtil.toBuffer( - (ethAbi as any).rawEncode( - ['uint256', 'uint256'], - [beginTimeSeconds.toString(), beginAmount.toString()], - ), - ), - ]), - ); - } + let dutchAuctionTestWrapper: DutchAuctionTestWrapper; + let defaultERC20MakerAssetData: string; before(async () => { await blockchainLifecycle.startAsync(); @@ -136,6 +125,7 @@ describe(ContractName.DutchAuction, () => { dutchAuctionInstance.address, provider, ); + dutchAuctionTestWrapper = new DutchAuctionTestWrapper(dutchAuctionInstance, provider); defaultMakerAssetAddress = erc20TokenA.address; const defaultTakerAssetAddress = wethContract.address; @@ -174,7 +164,7 @@ describe(ContractName.DutchAuction, () => { feeRecipientAddress, // taker address or sender address should be set to the ducth auction contract takerAddress: dutchAuctionContract.address, - makerAssetData: extendMakerAssetData( + makerAssetData: DutchAuctionWrapper.encodeDutchAuctionAssetData( assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), auctionBeginTimeSeconds, auctionBeginAmount, @@ -199,6 +189,7 @@ describe(ContractName.DutchAuction, () => { const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams); buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams); + defaultERC20MakerAssetData = assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress); }); after(async () => { await blockchainLifecycle.revertAsync(); @@ -215,49 +206,41 @@ describe(ContractName.DutchAuction, () => { describe('matchOrders', () => { it('should be worth the begin price at the begining of the auction', async () => { auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerAssetData: extendMakerAssetData( - assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), - }); - const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData( + defaultERC20MakerAssetData, + auctionBeginTimeSeconds, + auctionBeginAmount, + ); + sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData }); + const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); + expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds); expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount); expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); }); it('should be be worth the end price at the end of the auction', async () => { auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); + const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData( + defaultERC20MakerAssetData, + auctionBeginTimeSeconds, + auctionBeginAmount, + ); sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerAssetData: extendMakerAssetData( - assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), + makerAssetData, expirationTimeSeconds: auctionEndTimeSeconds, }); - const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); + expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds); expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount); expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); }); it('should match orders at current amount and send excess to buyer', async () => { - const beforeAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const beforeAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); buyOrder = await buyerOrderFactory.newSignedOrderAsync({ makerAssetAmount: beforeAuctionDetails.currentAmount.times(2), }); - await web3Wrapper.awaitTransactionSuccessAsync( - await dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), - ); - const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); + const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); const newBalances = await erc20Wrapper.getBalancesAsync(); expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal( constants.ZERO_AMOUNT, @@ -276,17 +259,8 @@ describe(ContractName.DutchAuction, () => { sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerFee: new BigNumber(1), }); - const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); + const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); const newBalances = await erc20Wrapper.getBalancesAsync(); expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), @@ -299,18 +273,9 @@ describe(ContractName.DutchAuction, () => { buyOrder = await buyerOrderFactory.newSignedOrderAsync({ makerFee: new BigNumber(1), }); - const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); + await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); const newBalances = await erc20Wrapper.getBalancesAsync(); - const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), ); @@ -321,24 +286,17 @@ describe(ContractName.DutchAuction, () => { it('should revert when auction expires', async () => { auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); + const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData( + defaultERC20MakerAssetData, + auctionBeginTimeSeconds, + auctionBeginAmount, + ); sellOrder = await sellerOrderFactory.newSignedOrderAsync({ expirationTimeSeconds: auctionEndTimeSeconds, - makerAssetData: extendMakerAssetData( - assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), + makerAssetData, }); return expectTransactionFailedAsync( - dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), + dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), RevertReason.AuctionExpired, ); }); @@ -347,15 +305,7 @@ describe(ContractName.DutchAuction, () => { makerAssetAmount: sellOrder.takerAssetAmount, }); return expectTransactionFailedAsync( - dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), + dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), RevertReason.AuctionInvalidAmount, ); }); @@ -364,38 +314,23 @@ describe(ContractName.DutchAuction, () => { takerAssetAmount: auctionBeginAmount.plus(1), }); return expectTransactionFailedAsync( - dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), + dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), RevertReason.AuctionInvalidAmount, ); }); it('begin time is less than end time', async () => { auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds); + const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData( + defaultERC20MakerAssetData, + auctionBeginTimeSeconds, + auctionBeginAmount, + ); sellOrder = await sellerOrderFactory.newSignedOrderAsync({ expirationTimeSeconds: auctionEndTimeSeconds, - makerAssetData: extendMakerAssetData( - assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), + makerAssetData, }); return expectTransactionFailedAsync( - dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), + dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), RevertReason.AuctionInvalidBeginTime, ); }); @@ -404,45 +339,30 @@ describe(ContractName.DutchAuction, () => { makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), }); return expectTransactionFailedAsync( - dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), + dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress), RevertReason.InvalidAssetData, ); }); + describe('ERC721', () => { it('should match orders when ERC721', async () => { const makerAssetId = erc721MakerAssetIds[0]; + const erc721MakerAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId); + const makerAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData( + erc721MakerAssetData, + auctionBeginTimeSeconds, + auctionBeginAmount, + ); sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetAmount: new BigNumber(1), - makerAssetData: extendMakerAssetData( - assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), + makerAssetData, }); buyOrder = await buyerOrderFactory.newSignedOrderAsync({ takerAssetAmount: new BigNumber(1), takerAssetData: sellOrder.makerAssetData, }); - await web3Wrapper.awaitTransactionSuccessAsync( - await dutchAuctionContract.matchOrders.sendTransactionAsync( - buyOrder, - sellOrder, - buyOrder.signature, - sellOrder.signature, - { - from: takerAddress, - }, - ), - ); - const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); + const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); const newBalances = await erc20Wrapper.getBalancesAsync(); // HACK gte used here due to a bug in ganache where the timestamp can change // between multiple calls to the same block. Which can move the amount in our case diff --git a/contracts/extensions/test/extensions/forwarder.ts b/contracts/extensions/test/extensions/forwarder.ts index 4027f493d..69939ed04 100644 --- a/contracts/extensions/test/extensions/forwarder.ts +++ b/contracts/extensions/test/extensions/forwarder.ts @@ -48,7 +48,6 @@ describe(ContractName.Forwarder, () => { let owner: string; let takerAddress: string; let feeRecipientAddress: string; - let otherAddress: string; let defaultMakerAssetAddress: string; let zrxAssetData: string; let wethAssetData: string; @@ -78,7 +77,7 @@ describe(ContractName.Forwarder, () => { before(async () => { await blockchainLifecycle.startAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress, otherAddress] = accounts); + const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts); const txHash = await web3Wrapper.sendTransactionAsync({ from: accounts[0], to: accounts[0], value: 0 }); const transaction = await web3Wrapper.getTransactionByHashAsync(txHash); diff --git a/contracts/extensions/test/extensions/order_matcher.ts b/contracts/extensions/test/extensions/order_matcher.ts new file mode 100644 index 000000000..acb46ced4 --- /dev/null +++ b/contracts/extensions/test/extensions/order_matcher.ts @@ -0,0 +1,818 @@ +import { + artifacts as protocolArtifacts, + ERC20ProxyContract, + ERC20Wrapper, + ERC721ProxyContract, + ExchangeContract, + ExchangeFillEventArgs, + ExchangeWrapper, +} from '@0x/contracts-protocol'; +import { + chaiSetup, + constants, + ERC20BalancesByOwner, + expectContractCreationFailedAsync, + expectTransactionFailedAsync, + LogDecoder, + OrderFactory, + provider, + sendTransactionResult, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { artifacts as tokenArtifacts, DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { OrderMatcherContract } from '../../generated-wrappers/order_matcher'; +import { artifacts } from '../../src/artifacts'; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +chaiSetup.configure(); +const expect = chai.expect; +// tslint:disable:no-unnecessary-type-assertion +describe('OrderMatcher', () => { + let makerAddressLeft: string; + let makerAddressRight: string; + let owner: string; + let takerAddress: string; + let feeRecipientAddressLeft: string; + let feeRecipientAddressRight: string; + + let erc20TokenA: DummyERC20TokenContract; + let erc20TokenB: DummyERC20TokenContract; + let zrxToken: DummyERC20TokenContract; + let exchange: ExchangeContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + let orderMatcher: OrderMatcherContract; + + let erc20BalancesByOwner: ERC20BalancesByOwner; + let exchangeWrapper: ExchangeWrapper; + let erc20Wrapper: ERC20Wrapper; + let orderFactoryLeft: OrderFactory; + let orderFactoryRight: OrderFactory; + + let leftMakerAssetData: string; + let leftTakerAssetData: string; + let defaultERC20MakerAssetAddress: string; + let defaultERC20TakerAssetAddress: string; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + // Create accounts + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + // Hack(albrow): Both Prettier and TSLint insert a trailing comma below + // but that is invalid syntax as of TypeScript version >= 2.8. We don't + // have the right fine-grained configuration options in TSLint, + // Prettier, or TypeScript, to reconcile this, so we will just have to + // wait for them to sort it out. We disable TSLint and Prettier for + // this part of the code for now. This occurs several times in this + // file. See https://github.com/prettier/prettier/issues/4624. + // prettier-ignore + const usedAddresses = ([ + owner, + makerAddressLeft, + makerAddressRight, + takerAddress, + feeRecipientAddressLeft, + // tslint:disable-next-line:trailing-comma + feeRecipientAddressRight + ] = _.slice(accounts, 0, 6)); + // Create wrappers + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + // Deploy ERC20 token & ERC20 proxy + const numDummyErc20ToDeploy = 3; + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, + ); + erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + // Deploy ERC721 proxy + erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync( + protocolArtifacts.ERC721Proxy, + provider, + txDefaults, + ); + // Depoy exchange + exchange = await ExchangeContract.deployFrom0xArtifactAsync( + protocolArtifacts.Exchange, + provider, + txDefaults, + assetDataUtils.encodeERC20AssetData(zrxToken.address), + ); + exchangeWrapper = new ExchangeWrapper(exchange, provider); + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + // Authorize ERC20 trades by exchange + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Deploy OrderMatcher + orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchange.address, + ); + // Set default addresses + defaultERC20MakerAssetAddress = erc20TokenA.address; + defaultERC20TakerAssetAddress = erc20TokenB.address; + leftMakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress); + leftTakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress); + // Set OrderMatcher balances and allowances + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenA.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenB.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftMakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftTakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Create default order parameters + const defaultOrderParamsLeft = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressLeft, + exchangeAddress: exchange.address, + makerAssetData: leftMakerAssetData, + takerAssetData: leftTakerAssetData, + feeRecipientAddress: feeRecipientAddressLeft, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const defaultOrderParamsRight = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressRight, + exchangeAddress: exchange.address, + makerAssetData: leftTakerAssetData, + takerAssetData: leftMakerAssetData, + feeRecipientAddress: feeRecipientAddressRight, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; + orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); + const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; + orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('constructor', () => { + it('should revert if assetProxy is unregistered', async () => { + const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( + protocolArtifacts.Exchange, + provider, + txDefaults, + constants.NULL_BYTES, + ); + return expectContractCreationFailedAsync( + (OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchangeInstance.address, + ) as any) as sendTransactionResult, + RevertReason.UnregisteredAssetProxy, + ); + }); + }); + describe('matchOrders', () => { + beforeEach(async () => { + erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); + }); + it('should revert if not called by owner', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: takerAddress, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.OnlyContractOwner, + ); + }); + it('should transfer the correct amounts when orders completely fill each other', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + }); + it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance); + }); + it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were never partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts, ...protocolArtifacts }); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + ); + const fillLogs = _.filter( + txReceipt.logs, + log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', + ); + // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. + expect(fillLogs.length).to.be.equal(2); + }); + it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were initially partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + await exchangeWrapper.fillOrderAsync(signedOrderLeft, takerAddress, { + takerAssetFillAmount: signedOrderLeft.takerAssetAmount.dividedToIntegerBy(5), + }); + await exchangeWrapper.fillOrderAsync(signedOrderRight, takerAddress, { + takerAssetFillAmount: signedOrderRight.takerAssetAmount.dividedToIntegerBy(5), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts, ...protocolArtifacts }); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + ); + const fillLogs = _.filter( + txReceipt.logs, + log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', + ); + // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. + expect(fillLogs.length).to.be.equal(2); + }); + it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18), + }); + const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18); + const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount + .times(signedOrderRight.makerAssetAmount) + .dividedToIntegerBy(signedOrderRight.takerAssetAmount); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount), + amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT, + leftTakerAssetSpreadAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + signedOrderRight.makerAssetData = constants.NULL_BYTES; + signedOrderRight.takerAssetData = constants.NULL_BYTES; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should revert with the correct reason if matchOrders call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.InvalidOrderSignature, + ); + }); + it('should revert with the correct reason if fillOrder call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Matcher will not have enough allowance to fill rightOrder + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, constants.ZERO_AMOUNT, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.TransferFailed, + ); + }); + }); + describe('withdrawAsset', () => { + it('should allow owner to withdraw ERC20 tokens', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: owner, + }), + ); + const newBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should allow owner to withdraw ERC721 tokens', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const tokenId = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Token.mint.sendTransactionAsync(orderMatcher.address, tokenId, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); + const withdrawAmount = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(assetData, withdrawAmount, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const erc721Owner = await erc721Token.ownerOf.callAsync(tokenId); + expect(erc721Owner).to.be.equal(owner); + }); + it('should revert if not called by owner', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await expectTransactionFailedAsync( + orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); + describe('approveAssetProxy', () => { + it('should be able to set an allowance for ERC20 tokens', async () => { + const allowance = new BigNumber(55465465426546); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, allowance, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newAllowance = await erc20TokenA.allowance.callAsync(orderMatcher.address, erc20Proxy.address); + expect(newAllowance).to.be.bignumber.equal(allowance); + }); + it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(2); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should revert if not called by owner', async () => { + const approval = new BigNumber(1); + await expectTransactionFailedAsync( + orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, approval, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); +}); +// tslint:disable:max-file-line-count +// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/protocol/test/exchange/order_validator.ts b/contracts/extensions/test/extensions/order_validator.ts index 8f53426db..82a6b937f 100644 --- a/contracts/protocol/test/exchange/order_validator.ts +++ b/contracts/extensions/test/extensions/order_validator.ts @@ -1,4 +1,13 @@ import { + artifacts as protocolArtifacts, + ERC20ProxyContract, + ERC20Wrapper, + ERC721ProxyContract, + ERC721Wrapper, + ExchangeContract, + ExchangeWrapper, +} from '@0x/contracts-protocol'; +import { chaiSetup, constants, OrderFactory, @@ -15,16 +24,8 @@ import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; import * as _ from 'lodash'; -import { - artifacts, - ERC20ProxyContract, - ERC20Wrapper, - ERC721ProxyContract, - ERC721Wrapper, - ExchangeContract, - ExchangeWrapper, - OrderValidatorContract, -} from '../../src'; +import { OrderValidatorContract } from '../../generated-wrappers/order_validator'; +import { artifacts } from '../../src/artifacts/index'; chaiSetup.configure(); const expect = chai.expect; @@ -80,7 +81,7 @@ describe('OrderValidator', () => { const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, + protocolArtifacts.Exchange, provider, txDefaults, zrxAssetData, diff --git a/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts b/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts new file mode 100644 index 000000000..c1e2f2070 --- /dev/null +++ b/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts @@ -0,0 +1,62 @@ +import { artifacts as protocolArtifacts } from '@0x/contracts-protocol'; +import { LogDecoder } from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; +import { DutchAuctionDetails, SignedOrder } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; +import { artifacts } from '../../src/artifacts'; + +export class DutchAuctionTestWrapper { + private readonly _dutchAuctionContract: DutchAuctionContract; + private readonly _web3Wrapper: Web3Wrapper; + private readonly _logDecoder: LogDecoder; + + constructor(contractInstance: DutchAuctionContract, provider: Provider) { + this._dutchAuctionContract = contractInstance; + this._web3Wrapper = new Web3Wrapper(provider); + this._logDecoder = new LogDecoder(this._web3Wrapper, { + ...artifacts, + ...tokensArtifacts, + ...protocolArtifacts, + }); + } + /** + * Matches the buy and sell orders at an amount given the following: the current block time, the auction + * start time and the auction begin amount. The sell order is a an order at the lowest amount + * at the end of the auction. Excess from the match is transferred to the seller. + * Over time the price moves from beginAmount to endAmount given the current block.timestamp. + * @param buyOrder The Buyer's order. This order is for the current expected price of the auction. + * @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction). + * @param from Address the transaction is being sent from. + * @return Transaction receipt with decoded logs. + */ + public async matchOrdersAsync( + buyOrder: SignedOrder, + sellOrder: SignedOrder, + from: string, + ): Promise<TransactionReceiptWithDecodedLogs> { + const txHash = await this._dutchAuctionContract.matchOrders.sendTransactionAsync( + buyOrder, + sellOrder, + buyOrder.signature, + sellOrder.signature, + { + from, + }, + ); + const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); + return tx; + } + /** + * Calculates the Auction Details for the given order + * @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction). + * @return The dutch auction details. + */ + public async getAuctionDetailsAsync(sellOrder: SignedOrder): Promise<DutchAuctionDetails> { + const auctionDetails = await this._dutchAuctionContract.getAuctionDetails.callAsync(sellOrder); + return auctionDetails; + } +} diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index a303e3f5c..ed9b4fbe1 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -9,7 +9,9 @@ "files": [ "./generated-artifacts/BalanceThresholdFilter.json", "./generated-artifacts/DutchAuction.json", - "./generated-artifacts/Forwarder.json" + "./generated-artifacts/Forwarder.json", + "./generated-artifacts/OrderMatcher.json", + "./generated-artifacts/OrderValidator.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol index e2da68919..a130e615f 100644 --- a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetData.sol @@ -17,7 +17,7 @@ */ // solhint-disable -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol index b25d2d75a..706412dd0 100644 --- a/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAssetProxy.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./IAuthorizable.sol"; diff --git a/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol b/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol index 96ee05dee..202173998 100644 --- a/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol +++ b/contracts/interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/Ownable/IOwnable.sol"; diff --git a/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol b/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol index 8db8d6f6c..b73881c07 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IAssetProxyDispatcher { diff --git a/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol b/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol index b92abba04..866e4c194 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IExchange.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "./IExchangeCore.sol"; diff --git a/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol b/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol index 0da73529c..ab4a62408 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IExchangeCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol b/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol index b88e158c3..5df46ea80 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IMatchOrders.sol @@ -15,7 +15,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol b/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol index 1fd0eccf0..c5a4a57e1 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/ISignatureValidator.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract ISignatureValidator { diff --git a/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol b/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol index 4446c55ce..aaaee389f 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/ITransactions.sol @@ -15,7 +15,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract ITransactions { diff --git a/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol b/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol index 2dd69100c..d214e54dd 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IValidator.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IValidator { diff --git a/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol b/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol index c97161ca6..c2db4a5b1 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IWallet.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IWallet { diff --git a/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol b/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol index 833bb7e88..0eeaa874b 100644 --- a/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol +++ b/contracts/interfaces/contracts/protocol/Exchange/IWrapperFunctions.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/interfaces/package.json b/contracts/interfaces/package.json index 4d3e4b7f9..15385a154 100644 --- a/contracts/interfaces/package.json +++ b/contracts/interfaces/package.json @@ -10,7 +10,8 @@ "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s compile generate_contract_wrappers", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/libs/compiler.json b/contracts/libs/compiler.json index cf7c52a73..349d3063b 100644 --- a/contracts/libs/compiler.json +++ b/contracts/libs/compiler.json @@ -18,5 +18,14 @@ } } }, - "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712"] + "contracts": [ + "TestLibs", + "LibOrder", + "LibMath", + "LibFillResults", + "LibAbiEncoder", + "LibEIP712", + "LibAssetProxyErrors", + "LibConstants" + ] } diff --git a/contracts/libs/contracts/libs/LibAbiEncoder.sol b/contracts/libs/contracts/libs/LibAbiEncoder.sol index 4aad37709..5422bfeec 100644 --- a/contracts/libs/contracts/libs/LibAbiEncoder.sol +++ b/contracts/libs/contracts/libs/LibAbiEncoder.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "./LibOrder.sol"; diff --git a/contracts/libs/contracts/libs/LibAddressArray.sol b/contracts/libs/contracts/libs/LibAddressArray.sol index ccae2ac5f..997ce85fa 100644 --- a/contracts/libs/contracts/libs/LibAddressArray.sol +++ b/contracts/libs/contracts/libs/LibAddressArray.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; diff --git a/contracts/libs/contracts/libs/LibAssetProxyErrors.sol b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol index 1d9a70cc1..96c48b5e3 100644 --- a/contracts/libs/contracts/libs/LibAssetProxyErrors.sol +++ b/contracts/libs/contracts/libs/LibAssetProxyErrors.sol @@ -17,7 +17,7 @@ */ // solhint-disable -pragma solidity 0.4.24; +pragma solidity ^0.4.24; /// @dev This contract documents the revert reasons used in the AssetProxy contracts. diff --git a/contracts/libs/contracts/libs/LibConstants.sol b/contracts/libs/contracts/libs/LibConstants.sol index 8d2732cd3..3efa3e1b3 100644 --- a/contracts/libs/contracts/libs/LibConstants.sol +++ b/contracts/libs/contracts/libs/LibConstants.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; // solhint-disable max-line-length diff --git a/contracts/libs/contracts/libs/LibEIP712.sol b/contracts/libs/contracts/libs/LibEIP712.sol index 203edc1fd..3a85ab3c0 100644 --- a/contracts/libs/contracts/libs/LibEIP712.sol +++ b/contracts/libs/contracts/libs/LibEIP712.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract LibEIP712 { diff --git a/contracts/libs/contracts/libs/LibExchangeErrors.sol b/contracts/libs/contracts/libs/LibExchangeErrors.sol index a0f75bc06..a160242c9 100644 --- a/contracts/libs/contracts/libs/LibExchangeErrors.sol +++ b/contracts/libs/contracts/libs/LibExchangeErrors.sol @@ -17,7 +17,7 @@ */ // solhint-disable -pragma solidity 0.4.24; +pragma solidity ^0.4.24; /// @dev This contract documents the revert reasons used in the Exchange contract. diff --git a/contracts/libs/contracts/libs/LibExchangeSelectors.sol b/contracts/libs/contracts/libs/LibExchangeSelectors.sol index edb4f9cbd..71640c609 100644 --- a/contracts/libs/contracts/libs/LibExchangeSelectors.sol +++ b/contracts/libs/contracts/libs/LibExchangeSelectors.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract LibExchangeSelectors { diff --git a/contracts/libs/contracts/libs/LibFillResults.sol b/contracts/libs/contracts/libs/LibFillResults.sol index fbd9950bf..74b7f7984 100644 --- a/contracts/libs/contracts/libs/LibFillResults.sol +++ b/contracts/libs/contracts/libs/LibFillResults.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; diff --git a/contracts/libs/contracts/libs/LibMath.sol b/contracts/libs/contracts/libs/LibMath.sol index b24876a9c..f14b1b34d 100644 --- a/contracts/libs/contracts/libs/LibMath.sol +++ b/contracts/libs/contracts/libs/LibMath.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; diff --git a/contracts/libs/contracts/libs/LibOrder.sol b/contracts/libs/contracts/libs/LibOrder.sol index 0fe7c2161..fcf5da5fb 100644 --- a/contracts/libs/contracts/libs/LibOrder.sol +++ b/contracts/libs/contracts/libs/LibOrder.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./LibEIP712.sol"; diff --git a/contracts/libs/package.json b/contracts/libs/package.json index fa4b6e523..ce7b97502 100644 --- a/contracts/libs/package.json +++ b/contracts/libs/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/multisig/package.json b/contracts/multisig/package.json index b338f67f7..2d7b4aa05 100644 --- a/contracts/multisig/package.json +++ b/contracts/multisig/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../packages/abi-gen-templates/contract.handlebars --partials '../../packages/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/protocol/CHANGELOG.json b/contracts/protocol/CHANGELOG.json index 2dca9794a..e283d7917 100644 --- a/contracts/protocol/CHANGELOG.json +++ b/contracts/protocol/CHANGELOG.json @@ -5,6 +5,14 @@ { "note": "Added LibAddressArray", "pr": 1383 + }, + { + "note": "Add validation and comments to MultiAssetProxy", + "pr": 1455 + }, + { + "note": "Move OrderValidator to extensions", + "pr": 1464 } ] }, diff --git a/contracts/protocol/compiler.json b/contracts/protocol/compiler.json index c05d62aba..10e5bb0a1 100644 --- a/contracts/protocol/compiler.json +++ b/contracts/protocol/compiler.json @@ -25,7 +25,6 @@ "Exchange", "MixinAuthorizable", "MultiAssetProxy", - "OrderValidator", "TestAssetProxyOwner", "TestAssetProxyDispatcher", "TestExchangeInternals", diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MixinAuthorizable.sol b/contracts/protocol/contracts/protocol/AssetProxy/MixinAuthorizable.sol index 08f9b94dc..b610ef709 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MixinAuthorizable.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MixinAuthorizable.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "./mixins/MAuthorizable.sol"; diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 42231e73b..5f559163c 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -33,6 +33,9 @@ contract MultiAssetProxy is function () external { + // NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification. + // It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively + // expensive and we therefore do not check for overflows in these scenarios. assembly { // The first 4 bytes of calldata holds the function selector let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) @@ -145,7 +148,7 @@ contract MultiAssetProxy is let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` - if iszero(eq(amountsLen, nestedAssetDataLen)) { + if sub(amountsLen, nestedAssetDataLen) { // Revert with `Error("LENGTH_MISMATCH")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) @@ -181,8 +184,11 @@ contract MultiAssetProxy is let amountsElement := calldataload(add(amountsContentsStart, i)) let totalAmount := mul(amountsElement, amount) - // Revert if multiplication resulted in an overflow - if iszero(eq(div(totalAmount, amount), amountsElement)) { + // Revert if `amount` != 0 and multiplication resulted in an overflow + if iszero(or( + iszero(amount), + eq(div(totalAmount, amount), amountsElement) + )) { // Revert with `Error("UINT256_OVERFLOW")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) @@ -230,7 +236,7 @@ contract MultiAssetProxy is // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 - if iszero(eq(currentAssetProxyId, assetProxyId)) { + if sub(currentAssetProxyId, assetProxyId) { // Update `assetProxyId` assetProxyId := currentAssetProxyId // To lookup a value in a mapping, we load from the storage location keccak256(k, p), diff --git a/contracts/protocol/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol b/contracts/protocol/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol index fe77048ce..6c3f70500 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/mixins/MAuthorizable.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-interfaces/contracts/protocol/AssetProxy/IAuthorizable.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol b/contracts/protocol/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol index 36ab39b45..2922a8c1a 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "./mixins/MAssetProxyDispatcher.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinExchangeCore.sol b/contracts/protocol/contracts/protocol/Exchange/MixinExchangeCore.sol index 68d6a3897..72bcebc62 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinExchangeCore.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinExchangeCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinMatchOrders.sol b/contracts/protocol/contracts/protocol/Exchange/MixinMatchOrders.sol index fc6d73482..2627b82fd 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinMatchOrders.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinMatchOrders.sol @@ -11,7 +11,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinSignatureValidator.sol b/contracts/protocol/contracts/protocol/Exchange/MixinSignatureValidator.sol index 80b4c0755..b40aa1412 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinSignatureValidator.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinSignatureValidator.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinTransactions.sol b/contracts/protocol/contracts/protocol/Exchange/MixinTransactions.sol index 87c614382..1ac5b1a5e 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinTransactions.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinTransactions.sol @@ -15,7 +15,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-libs/contracts/libs/LibExchangeErrors.sol"; import "./mixins/MSignatureValidator.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/MixinWrapperFunctions.sol b/contracts/protocol/contracts/protocol/Exchange/MixinWrapperFunctions.sol index 2d43432ff..6fc6ee999 100644 --- a/contracts/protocol/contracts/protocol/Exchange/MixinWrapperFunctions.sol +++ b/contracts/protocol/contracts/protocol/Exchange/MixinWrapperFunctions.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index fe3d03326..05c2c4c0b 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IAssetProxyDispatcher.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MExchangeCore.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MExchangeCore.sol index 215284900..09dc491cd 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MExchangeCore.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MExchangeCore.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MMatchOrders.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MMatchOrders.sol index 1eb4be329..56ee895c4 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MMatchOrders.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MMatchOrders.sol @@ -15,7 +15,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MSignatureValidator.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MSignatureValidator.sol index a933976d1..6407760d4 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MSignatureValidator.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-interfaces/contracts/protocol/Exchange/ISignatureValidator.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MTransactions.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MTransactions.sol index a6b0fdc85..04dd716d7 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MTransactions.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MTransactions.sol @@ -15,7 +15,7 @@ limitations under the License. */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-interfaces/contracts/protocol/Exchange/ITransactions.sol"; diff --git a/contracts/protocol/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol b/contracts/protocol/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol index 101e7cb82..2ff3b8790 100644 --- a/contracts/protocol/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol +++ b/contracts/protocol/contracts/protocol/Exchange/mixins/MWrapperFunctions.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; pragma experimental ABIEncoderV2; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; diff --git a/contracts/protocol/package.json b/contracts/protocol/package.json index 122ce07c3..891838e54 100644 --- a/contracts/protocol/package.json +++ b/contracts/protocol/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", @@ -31,7 +32,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(AssetProxyOwner|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiAssetProxy|OrderValidator|TestSignatureValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestStaticCallReceiver).json" + "abis": "generated-artifacts/@(AssetProxyOwner|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiAssetProxy|TestSignatureValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestStaticCallReceiver).json" }, "repository": { "type": "git", diff --git a/contracts/protocol/src/artifacts/index.ts b/contracts/protocol/src/artifacts/index.ts index 1d53ceb04..c5d12f10b 100644 --- a/contracts/protocol/src/artifacts/index.ts +++ b/contracts/protocol/src/artifacts/index.ts @@ -6,7 +6,6 @@ import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; -import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; import * as TestExchangeInternals from '../../generated-artifacts/TestExchangeInternals.json'; @@ -20,7 +19,6 @@ export const artifacts = { Exchange: Exchange as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, - OrderValidator: OrderValidator as ContractArtifact, TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, TestExchangeInternals: TestExchangeInternals as ContractArtifact, diff --git a/contracts/protocol/src/wrappers/index.ts b/contracts/protocol/src/wrappers/index.ts index ac951d269..01b121054 100644 --- a/contracts/protocol/src/wrappers/index.ts +++ b/contracts/protocol/src/wrappers/index.ts @@ -3,7 +3,6 @@ export * from '../../generated-wrappers/erc20_proxy'; export * from '../../generated-wrappers/erc721_proxy'; export * from '../../generated-wrappers/exchange'; export * from '../../generated-wrappers/mixin_authorizable'; -export * from '../../generated-wrappers/order_validator'; export * from '../../generated-wrappers/test_asset_proxy_dispatcher'; export * from '../../generated-wrappers/test_asset_proxy_owner'; export * from '../../generated-wrappers/test_exchange_internals'; diff --git a/contracts/protocol/test/asset_proxy/proxies.ts b/contracts/protocol/test/asset_proxy/proxies.ts index c4bd95905..bbb44f402 100644 --- a/contracts/protocol/test/asset_proxy/proxies.ts +++ b/contracts/protocol/test/asset_proxy/proxies.ts @@ -12,6 +12,7 @@ import { import { artifacts as tokensArtifacts, DummyERC20TokenContract, + DummyERC20TokenTransferEventArgs, DummyERC721ReceiverContract, DummyERC721TokenContract, DummyMultipleReturnERC20TokenContract, @@ -22,6 +23,7 @@ import { assetDataUtils } from '@0x/order-utils'; import { RevertReason } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; @@ -713,7 +715,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -738,6 +740,36 @@ describe('Asset Transfer Proxies', () => { erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), ); }); + it('should dispatch an ERC20 transfer when input amount is 0', async () => { + const inputAmount = constants.ZERO_AMOUNT; + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const amounts = [erc20Amount]; + const nestedAssetData = [erc20AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); + const tx = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + ); + expect(tx.logs.length).to.be.equal(1); + const log = tx.logs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>; + const transferEventName = 'Transfer'; + expect(log.event).to.equal(transferEventName); + expect(log.args._value).to.be.bignumber.equal(constants.ZERO_AMOUNT); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances).to.deep.equal(erc20Balances); + }); it('should successfully transfer multiple of the same ERC20 token', async () => { const inputAmount = new BigNumber(1); const erc20Amount1 = new BigNumber(10); @@ -746,7 +778,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -779,7 +811,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -817,7 +849,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc721Amount]; const nestedAssetData = [erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -849,7 +881,7 @@ describe('Asset Transfer Proxies', () => { const erc721Amount = new BigNumber(1); const amounts = [erc721Amount, erc721Amount]; const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -881,7 +913,7 @@ describe('Asset Transfer Proxies', () => { const erc721Amount = new BigNumber(1); const amounts = [erc721Amount, erc721Amount]; const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -914,7 +946,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -952,10 +984,7 @@ describe('Asset Transfer Proxies', () => { const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; const extraData = '0102030405060708'; - const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData( - amounts, - nestedAssetData, - )}${extraData}`; + const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`; const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -992,7 +1021,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1053,7 +1082,7 @@ describe('Asset Transfer Proxies', () => { erc721AssetData3, erc721AssetData4, ]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1111,7 +1140,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1137,6 +1166,7 @@ describe('Asset Transfer Proxies', () => { const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`; const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, invalidErc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1160,6 +1190,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1182,7 +1213,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1206,6 +1237,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = '0x123456'; const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1230,7 +1262,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, diff --git a/contracts/protocol/test/exchange/core.ts b/contracts/protocol/test/exchange/core.ts index 9b948f991..42db8623d 100644 --- a/contracts/protocol/test/exchange/core.ts +++ b/contracts/protocol/test/exchange/core.ts @@ -1,4 +1,3 @@ -import { artifacts as interfacesArtifacts, IAssetDataContract } from '@0x/contracts-interfaces'; import { chaiSetup, constants, @@ -43,11 +42,6 @@ import { ExchangeWrapper } from '../utils/exchange_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const assetDataInterface = new IAssetDataContract( - interfacesArtifacts.IAssetData.compilerOutput.abi, - constants.NULL_ADDRESS, - provider, -); // tslint:disable:no-unnecessary-type-assertion describe('Exchange core', () => { let makerAddress: string; @@ -777,10 +771,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(1); const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const takerAssetAmount = new BigNumber(10); @@ -830,10 +821,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(1); const takerAmounts = [new BigNumber(10), new BigNumber(1)]; const takerAssetId = erc721TakerAssetIds[0]; @@ -841,10 +829,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(zrxToken.address), assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); + const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData); const takerAssetAmount = new BigNumber(1); signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData, @@ -900,10 +885,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(30); const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const takerAssetAmount = new BigNumber(10); @@ -980,10 +962,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); + const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData); const takerAssetAmount = new BigNumber(30); const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const makerAssetAmount = new BigNumber(10); diff --git a/contracts/protocol/tsconfig.json b/contracts/protocol/tsconfig.json index 989d3ef2b..db872fc32 100644 --- a/contracts/protocol/tsconfig.json +++ b/contracts/protocol/tsconfig.json @@ -13,7 +13,6 @@ "./generated-artifacts/Exchange.json", "./generated-artifacts/MixinAuthorizable.json", "./generated-artifacts/MultiAssetProxy.json", - "./generated-artifacts/OrderValidator.json", "./generated-artifacts/TestAssetProxyDispatcher.json", "./generated-artifacts/TestAssetProxyOwner.json", "./generated-artifacts/TestExchangeInternals.json", diff --git a/contracts/test-utils/src/constants.ts b/contracts/test-utils/src/constants.ts index d2c3ab512..f631dc81a 100644 --- a/contracts/test-utils/src/constants.ts +++ b/contracts/test-utils/src/constants.ts @@ -29,6 +29,7 @@ export const constants = { MAX_TOKEN_TRANSFERFROM_GAS: 80000, MAX_TOKEN_APPROVE_GAS: 60000, MAX_TRANSFER_FROM_GAS: 150000, + MAX_MATCH_ORDERS_GAS: 400000, DUMMY_TOKEN_NAME: '', DUMMY_TOKEN_SYMBOL: '', DUMMY_TOKEN_DECIMALS: new BigNumber(18), diff --git a/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol index 725d304df..190eead76 100644 --- a/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol +++ b/contracts/tokens/contracts/tokens/ERC20Token/ERC20Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./IERC20Token.sol"; diff --git a/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol index 258d47393..9f9b89585 100644 --- a/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol +++ b/contracts/tokens/contracts/tokens/ERC20Token/IERC20Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IERC20Token { diff --git a/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol index 58bccb5a1..a66031f03 100644 --- a/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol +++ b/contracts/tokens/contracts/tokens/ERC20Token/MintableERC20Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol"; import "./UnlimitedAllowanceERC20Token.sol"; diff --git a/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol b/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol index 2e5bd4348..869d16147 100644 --- a/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol +++ b/contracts/tokens/contracts/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "../ERC20Token/ERC20Token.sol"; diff --git a/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol index 600cee1ab..c46bd0af4 100644 --- a/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol +++ b/contracts/tokens/contracts/tokens/ERC721Token/ERC721Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./IERC721Token.sol"; import "./IERC721Receiver.sol"; diff --git a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol index 8e0e32ab2..c3ad35ede 100644 --- a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol +++ b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Receiver.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IERC721Receiver { diff --git a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol index ac992c80d..56a3314df 100644 --- a/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol +++ b/contracts/tokens/contracts/tokens/ERC721Token/IERC721Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IERC721Token { diff --git a/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol b/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol index bc5cd2cc2..27326d857 100644 --- a/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol +++ b/contracts/tokens/contracts/tokens/ERC721Token/MintableERC721Token.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./ERC721Token.sol"; diff --git a/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol b/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol index 9e2e68766..32baa3eb0 100644 --- a/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol +++ b/contracts/tokens/contracts/tokens/EtherToken/IEtherToken.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "../ERC20Token/IERC20Token.sol"; diff --git a/contracts/tokens/package.json b/contracts/tokens/package.json index 6f8a366dd..79afc4820 100644 --- a/contracts/tokens/package.json +++ b/contracts/tokens/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/tokens/src/artifacts/index.ts b/contracts/tokens/src/artifacts/index.ts index 63b808748..977d01c3e 100644 --- a/contracts/tokens/src/artifacts/index.ts +++ b/contracts/tokens/src/artifacts/index.ts @@ -22,6 +22,7 @@ import * as UnlimitedAllowanceToken_v1 from '../../generated-artifacts/Unlimited import * as WETH9 from '../../generated-artifacts/WETH9.json'; import * as ZRXToken from '../../generated-artifacts/ZRXToken.json'; +// tslint:disable:no-unnecessary-type-assertion export const artifacts = { DummyERC20Token: DummyERC20Token as ContractArtifact, DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact, diff --git a/contracts/utils/contracts/utils/LibBytes/LibBytes.sol b/contracts/utils/contracts/utils/LibBytes/LibBytes.sol index 369f588ad..4ee6228d5 100644 --- a/contracts/utils/contracts/utils/LibBytes/LibBytes.sol +++ b/contracts/utils/contracts/utils/LibBytes/LibBytes.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; library LibBytes { diff --git a/contracts/utils/contracts/utils/Ownable/IOwnable.sol b/contracts/utils/contracts/utils/Ownable/IOwnable.sol index 5deb13497..c0cbfddfd 100644 --- a/contracts/utils/contracts/utils/Ownable/IOwnable.sol +++ b/contracts/utils/contracts/utils/Ownable/IOwnable.sol @@ -1,4 +1,4 @@ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract IOwnable { diff --git a/contracts/utils/contracts/utils/Ownable/Ownable.sol b/contracts/utils/contracts/utils/Ownable/Ownable.sol index 0c830be68..aa74a72d2 100644 --- a/contracts/utils/contracts/utils/Ownable/Ownable.sol +++ b/contracts/utils/contracts/utils/Ownable/Ownable.sol @@ -1,4 +1,4 @@ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; import "./IOwnable.sol"; diff --git a/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol b/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol index 9f98a7a16..1a02c88a4 100644 --- a/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol +++ b/contracts/utils/contracts/utils/ReentrancyGuard/ReentrancyGuard.sol @@ -16,7 +16,7 @@ */ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract ReentrancyGuard { diff --git a/contracts/utils/contracts/utils/SafeMath/SafeMath.sol b/contracts/utils/contracts/utils/SafeMath/SafeMath.sol index 2855edb9d..d7a4a603e 100644 --- a/contracts/utils/contracts/utils/SafeMath/SafeMath.sol +++ b/contracts/utils/contracts/utils/SafeMath/SafeMath.sol @@ -1,4 +1,4 @@ -pragma solidity 0.4.24; +pragma solidity ^0.4.24; contract SafeMath { diff --git a/contracts/utils/package.json b/contracts/utils/package.json index a776bdfbb..cf94af0f6 100644 --- a/contracts/utils/package.json +++ b/contracts/utils/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", |