diff options
29 files changed, 1099 insertions, 323 deletions
diff --git a/packages/contracts/package.json b/packages/contracts/package.json index a85e9aa4f..410735359 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -30,9 +30,9 @@ }, "config": { "abis": - "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|TestAssetProxyDispatcher|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes|Authorizable).json", + "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|TestAssetProxyDispatcher|TestLibs|TestSignatureValidator|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes|Authorizable).json", "contracts": - "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,TestAssetProxyDispatcher,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes,Authorizable" + "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,TestAssetProxyDispatcher,TestLibs,TestSignatureValidator,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes,Authorizable" }, "repository": { "type": "git", @@ -76,7 +76,6 @@ "ethereumjs-util": "^5.1.1", "ethers-contracts": "^2.2.1", "lodash": "^4.17.4", - "web3": "^0.20.0", - "zeppelin-solidity": "^1.8.0" + "web3": "^0.20.0" } } diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol index 466d0b871..eeb7e6710 100644 --- a/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol @@ -21,7 +21,7 @@ pragma solidity ^0.4.21; import "../IAssetProxy.sol"; import "../../../utils/LibBytes/LibBytes.sol"; import "../../../utils/Authorizable/Authorizable.sol"; -import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol"; +import "../../../tokens/ERC721Token/ERC721Token.sol"; contract ERC721Proxy is LibBytes, diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol index caf48bfca..e78446b99 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol @@ -27,17 +27,16 @@ import "./MixinAssetProxyDispatcher.sol"; import "./MixinTransactions.sol"; contract Exchange is + MixinWrapperFunctions, MixinExchangeCore, - MixinSignatureValidator, MixinSettlement, - MixinWrapperFunctions, - MixinAssetProxyDispatcher, - MixinTransactions + MixinSignatureValidator, + MixinTransactions, + MixinAssetProxyDispatcher { string constant public VERSION = "2.0.1-alpha"; - function Exchange( - bytes memory _zrxProxyData) + function Exchange(bytes memory _zrxProxyData) public MixinExchangeCore() MixinSignatureValidator() diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol b/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol index de3989a96..d5641a09f 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/ISigner.sol @@ -23,7 +23,8 @@ contract ISigner { function isValidSignature( bytes32 hash, - bytes memory signature) - public view + bytes signature) + external + view returns (bool isValid); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibFillResults.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibFillResults.sol new file mode 100644 index 000000000..41096f448 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibFillResults.sol @@ -0,0 +1,46 @@ +/* + + 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.21; +pragma experimental ABIEncoderV2; + +import "../../utils/SafeMath/SafeMath.sol"; + +contract LibFillResults is SafeMath { + + struct FillResults { + uint256 makerAssetFilledAmount; + uint256 takerAssetFilledAmount; + uint256 makerFeePaid; + uint256 takerFeePaid; + } + + /// @dev Adds properties of both FillResults instances. + /// Modifies the first FillResults instance specified. + /// @param totalFillResults Fill results instance that will be added onto. + /// @param singleFillResults Fill results instance that will be added to totalFillResults. + function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) + internal + pure + { + totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount); + totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount); + totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid); + totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid); + } +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.sol new file mode 100644 index 000000000..3b553ebbc --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibMath.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.21; +pragma experimental ABIEncoderV2; + +import "../../utils/SafeMath/SafeMath.sol"; + +contract LibMath is SafeMath { + + /// @dev Calculates partial value given a numerator and denominator. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to calculate partial of. + /// @return Partial value of target. + function getPartialAmount( + uint256 numerator, + uint256 denominator, + uint256 target) + internal + pure + returns (uint256 partialAmount) + { + partialAmount = safeDiv( + safeMul(numerator, target), + denominator + ); + return partialAmount; + } + + /// @dev Checks if rounding error > 0.1%. + /// @param numerator Numerator. + /// @param denominator Denominator. + /// @param target Value to multiply with numerator/denominator. + /// @return Rounding error is present. + function isRoundingError( + uint256 numerator, + uint256 denominator, + uint256 target) + internal + pure + returns (bool isError) + { + uint256 remainder = mulmod(target, numerator, denominator); + if (remainder == 0) { + return false; // No rounding error. + } + + uint256 errPercentageTimes1000000 = safeDiv( + safeMul(remainder, 1000000), + safeMul(numerator, target) + ); + isError = errPercentageTimes1000000 > 1000; + return isError; + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol index a98d7cbeb..df974e177 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol @@ -55,8 +55,9 @@ contract LibOrder { /// @dev Calculates Keccak-256 hash of the order. /// @param order The order structure. /// @return Keccak-256 EIP712 hash of the order. - function getOrderHash(Order order) - public view + function getOrderHash(Order memory order) + internal + view returns (bytes32 orderHash) { // TODO: EIP712 is not finalized yet diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol index eab8e04a1..b03bf464a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -18,9 +18,9 @@ pragma solidity ^0.4.21; -import "./mixins/MAssetProxyDispatcher.sol"; -import "../AssetProxy/IAssetProxy.sol"; import "../../utils/Ownable/Ownable.sol"; +import "../AssetProxy/IAssetProxy.sol"; +import "./mixins/MAssetProxyDispatcher.sol"; contract MixinAssetProxyDispatcher is Ownable, @@ -78,7 +78,8 @@ contract MixinAssetProxyDispatcher is /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(uint8 assetProxyId) - external view + external + view returns (address) { IAssetProxy assetProxy = assetProxies[assetProxyId]; diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol index 80120bc74..4ca271b2a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol @@ -19,27 +19,27 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; +import "./LibFillResults.sol"; +import "./LibOrder.sol"; +import "./LibErrors.sol"; +import "./LibMath.sol"; import "./mixins/MExchangeCore.sol"; import "./mixins/MSettlement.sol"; import "./mixins/MSignatureValidator.sol"; import "./mixins/MTransactions.sol"; -import "./LibOrder.sol"; -import "./LibErrors.sol"; -import "./LibPartialAmount.sol"; -import "../../utils/SafeMath/SafeMath.sol"; /// @dev Provides MExchangeCore /// @dev Consumes MSettlement /// @dev Consumes MSignatureValidator contract MixinExchangeCore is LibOrder, + LibFillResults, + LibErrors, + LibMath, MExchangeCore, MSettlement, MSignatureValidator, - MTransactions, - SafeMath, - LibErrors, - LibPartialAmount + MTransactions { // Mapping of orderHash => amount of takerAsset already bought by maker mapping (bytes32 => uint256) public filled; @@ -217,28 +217,6 @@ contract MixinExchangeCore is emit CancelUpTo(msg.sender, newMakerEpoch); } - /// @dev Checks if rounding error > 0.1%. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to multiply with numerator/denominator. - /// @return Rounding error is present. - function isRoundingError(uint256 numerator, uint256 denominator, uint256 target) - public pure - returns (bool isError) - { - uint256 remainder = mulmod(target, numerator, denominator); - if (remainder == 0) { - return false; // No rounding error. - } - - uint256 errPercentageTimes1000000 = safeDiv( - safeMul(remainder, 1000000), - safeMul(numerator, target) - ); - isError = errPercentageTimes1000000 > 1000; - return isError; - } - /// @dev Logs a Fill event with the given arguments. /// The sole purpose of this function is to get around the stack variable limit. function emitFillEvent( diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol index ae41c7a86..cab2ccfb6 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol @@ -21,19 +21,21 @@ pragma experimental ABIEncoderV2; import "./mixins/MSettlement.sol"; import "./mixins/MAssetProxyDispatcher.sol"; -import "./LibPartialAmount.sol"; +import "./LibOrder.sol"; +import "./LibMath.sol"; import "../AssetProxy/IAssetProxy.sol"; /// @dev Provides MixinSettlement contract MixinSettlement is + LibMath, MSettlement, - MAssetProxyDispatcher, - LibPartialAmount + MAssetProxyDispatcher { - bytes ZRX_PROXY_DATA; + bytes internal ZRX_PROXY_DATA; function zrxProxyData() - external view + external + view returns (bytes memory) { return ZRX_PROXY_DATA; @@ -46,7 +48,7 @@ contract MixinSettlement is } function settleOrder( - Order memory order, + LibOrder.Order memory order, address takerAddress, uint256 takerAssetFilledAmount) internal diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol index 48e7be424..690a70820 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol @@ -23,9 +23,8 @@ import "./mixins/MSignatureValidator.sol"; import "./ISigner.sol"; /// @dev Provides MSignatureValidator -contract MixinSignatureValidator is - MSignatureValidator -{ +contract MixinSignatureValidator is MSignatureValidator { + enum SignatureType { Illegal, // Default value Invalid, @@ -49,7 +48,8 @@ contract MixinSignatureValidator is bytes32 hash, address signer, bytes memory signature) - public view + internal + view returns (bool isValid) { // TODO: Domain separation: make hash depend on role. (Taker sig should not be valid as maker sig, etc.) @@ -166,8 +166,8 @@ contract MixinSignatureValidator is function preSign( bytes32 hash, address signer, - bytes memory signature) - public + bytes signature) + external { require(isValidSignature(hash, signer, signature)); preSigned[hash][signer] = true; diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol index 4d6ba17dd..105c0f0a0 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol @@ -19,17 +19,19 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "./mixins/MExchangeCore.sol"; -import "./LibPartialAmount.sol"; -import "../../utils/SafeMath/SafeMath.sol"; import "../../utils/LibBytes/LibBytes.sol"; +import "./mixins/MExchangeCore.sol"; +import "./LibMath.sol"; +import "./LibOrder.sol"; +import "./LibFillResults.sol"; /// @dev Consumes MExchangeCore contract MixinWrapperFunctions is - MExchangeCore, - SafeMath, + LibOrder, + LibFillResults, + LibMath, LibBytes, - LibPartialAmount + MExchangeCore { /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. /// @param order Order struct containing order specifications. @@ -489,19 +491,4 @@ contract MixinWrapperFunctions is cancelOrder(orders[i]); } } - - /// @dev Adds properties of both FillResults instances. - /// Modifies the first FillResults instance specified. - /// @param totalFillResults Fill results instance that will be added onto. - /// @param singleFillResults Fill results instance that will be added to totalFillResults. - function addFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) - internal - pure - { - totalFillResults.makerAssetFilledAmount = safeAdd(totalFillResults.makerAssetFilledAmount, singleFillResults.makerAssetFilledAmount); - totalFillResults.takerAssetFilledAmount = safeAdd(totalFillResults.takerAssetFilledAmount, singleFillResults.takerAssetFilledAmount); - totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid); - totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid); - } - } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol index a46be9d0f..6d0a3cd38 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -55,6 +55,7 @@ contract MAssetProxyDispatcher { /// @param assetProxyId Id of the asset proxy. /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. function getAssetProxy(uint8 assetProxyId) - external view + external + view returns (address); } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol index 9a7f80109..656df079e 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol @@ -20,24 +20,18 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; import "../LibOrder.sol"; +import "../LibFillResults.sol"; -contract MExchangeCore is LibOrder { - - struct FillResults { - uint256 makerAssetFilledAmount; - uint256 takerAssetFilledAmount; - uint256 makerFeePaid; - uint256 takerFeePaid; - } +contract MExchangeCore { function fillOrder( - Order memory order, + LibOrder.Order memory order, uint256 takerAssetFillAmount, bytes memory signature) public - returns (FillResults memory fillResults); + returns (LibFillResults.FillResults memory fillResults); - function cancelOrder(Order memory order) + function cancelOrder(LibOrder.Order memory order) public returns (bool); diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol index 172138f2d..0d7e59a9a 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSettlement.sol @@ -21,10 +21,10 @@ pragma experimental ABIEncoderV2; import "../LibOrder.sol"; -contract MSettlement is LibOrder { +contract MSettlement { function settleOrder( - Order memory order, + LibOrder.Order memory order, address takerAddress, uint256 takerAssetFilledAmount) internal diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol index 3f020e0a4..043a7da9c 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol @@ -30,6 +30,7 @@ contract MSignatureValidator { bytes32 hash, address signer, bytes memory signature) - public view + internal + view returns (bool isValid); } diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol index 7a635f15f..4fbaa6b74 100644 --- a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol +++ b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol @@ -17,7 +17,7 @@ */ pragma solidity ^0.4.21; -import "zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol"; +import "../../tokens/ERC721Token/ERC721Token.sol"; import "../../utils/Ownable/Ownable.sol"; contract DummyERC721Token is diff --git a/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol new file mode 100644 index 000000000..a5b327a90 --- /dev/null +++ b/packages/contracts/src/contracts/current/test/TestLibs/TestLibs.sol @@ -0,0 +1,80 @@ +/* + + 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.21; +pragma experimental ABIEncoderV2; + +import "../../protocol/Exchange/LibMath.sol"; +import "../../protocol/Exchange/LibOrder.sol"; +import "../../protocol/Exchange/LibFillResults.sol"; + +contract TestLibs is + LibMath, + LibOrder, + LibFillResults +{ + function publicGetPartialAmount( + uint256 numerator, + uint256 denominator, + uint256 target) + public + pure + returns (uint256 partialAmount) + { + partialAmount = getPartialAmount( + numerator, + denominator, + target + ); + return partialAmount; + } + + function publicIsRoundingError( + uint256 numerator, + uint256 denominator, + uint256 target) + public + pure + returns (bool isError) + { + isError = isRoundingError( + numerator, + denominator, + target + ); + return isError; + } + + function publicGetOrderHash(Order memory order) + public + view + returns (bytes32 orderHash) + { + orderHash = getOrderHash(order); + return orderHash; + } + + function publicAddFillResults(FillResults memory totalFillResults, FillResults memory singleFillResults) + public + pure + returns (FillResults memory) + { + addFillResults(totalFillResults, singleFillResults); + return totalFillResults; + } +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol index 2ff244e98..4ba04b64e 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol +++ b/packages/contracts/src/contracts/current/test/TestSignatureValidator/TestSignatureValidator.sol @@ -19,20 +19,23 @@ pragma solidity ^0.4.21; pragma experimental ABIEncoderV2; -import "../../utils/SafeMath/SafeMath.sol"; - -contract LibPartialAmount is SafeMath { - - /// @dev Calculates partial value given a numerator and denominator. - /// @param numerator Numerator. - /// @param denominator Denominator. - /// @param target Value to calculate partial of. - /// @return Partial value of target. - function getPartialAmount(uint256 numerator, uint256 denominator, uint256 target) - public pure - returns (uint256 partialAmount) +import "../../protocol/Exchange/MixinSignatureValidator.sol"; + +contract TestSignatureValidator is MixinSignatureValidator { + + function publicIsValidSignature( + bytes32 hash, + address signer, + bytes memory signature) + public + view + returns (bool isValid) { - partialAmount = safeDiv(safeMul(numerator, target), denominator); - return partialAmount; + isValid = isValidSignature( + hash, + signer, + signature + ); + return isValid; } -} +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol new file mode 100644 index 000000000..3bf064c3f --- /dev/null +++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/ERC721Token.sol @@ -0,0 +1,406 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Smart Contract Solutions, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.21; + +import "./IERC721Token.sol"; +import "./IERC721Receiver.sol"; +import "../../utils/SafeMath/SafeMath.sol"; + +/** + * @title ERC721 Non-Fungible Token Standard basic implementation + * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md + * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721BasicToken.sol + */ +contract ERC721Token is + IERC721Token, + SafeMath +{ + // Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` + // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` + bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba; + + // Mapping from token ID to owner + mapping (uint256 => address) internal tokenOwner; + + // Mapping from token ID to approved address + mapping (uint256 => address) internal tokenApprovals; + + // Mapping from owner to number of owned token + mapping (address => uint256) internal ownedTokensCount; + + // Mapping from owner to operator approvals + mapping (address => mapping (address => bool)) internal operatorApprovals; + + /** + * @dev Guarantees msg.sender is owner of the given token + * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender + */ + modifier onlyOwnerOf(uint256 _tokenId) { + require(ownerOf(_tokenId) == msg.sender); + _; + } + + /** + * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator + * @param _tokenId uint256 ID of the token to validate + */ + modifier canTransfer(uint256 _tokenId) { + require(isApprovedOrOwner(msg.sender, _tokenId)); + _; + } + + function ERC721Token( + string _name, + string _symbol) + public + { + name_ = _name; + symbol_ = _symbol; + } + + /** + * @dev Gets the token name + * @return string representing the token name + */ + function name() + public + view + returns (string) + { + return name_; + } + + /** + * @dev Gets the token symbol + * @return string representing the token symbol + */ + function symbol() + public + view + returns (string) + { + return symbol_; + } + + /** + * @dev Gets the balance of the specified address + * @param _owner address to query the balance of + * @return uint256 representing the amount owned by the passed address + */ + function balanceOf(address _owner) + public + view + returns (uint256) + { + require(_owner != address(0)); + return ownedTokensCount[_owner]; + } + + /** + * @dev Gets the owner of the specified token ID + * @param _tokenId uint256 ID of the token to query the owner of + * @return owner address currently marked as the owner of the given token ID + */ + function ownerOf(uint256 _tokenId) + public + view + returns (address) + { + address owner = tokenOwner[_tokenId]; + require(owner != address(0)); + return owner; + } + + /** + * @dev Returns whether the specified token exists + * @param _tokenId uint256 ID of the token to query the existance of + * @return whether the token exists + */ + function exists(uint256 _tokenId) + public + view + returns (bool) + { + address owner = tokenOwner[_tokenId]; + return owner != address(0); + } + + /** + * @dev Approves another address to transfer the given token ID + * @dev The zero address indicates there is no approved address. + * @dev There can only be one approved address per token at a given time. + * @dev Can only be called by the token owner or an approved operator. + * @param _to address to be approved for the given token ID + * @param _tokenId uint256 ID of the token to be approved + */ + function approve(address _to, uint256 _tokenId) + public + { + address owner = ownerOf(_tokenId); + require(_to != owner); + require(msg.sender == owner || isApprovedForAll(owner, msg.sender)); + + if (getApproved(_tokenId) != address(0) || _to != address(0)) { + tokenApprovals[_tokenId] = _to; + emit Approval(owner, _to, _tokenId); + } + } + + /** + * @dev Gets the approved address for a token ID, or zero if no address set + * @param _tokenId uint256 ID of the token to query the approval of + * @return address currently approved for a the given token ID + */ + function getApproved(uint256 _tokenId) + public + view + returns (address) + { + return tokenApprovals[_tokenId]; + } + + /** + * @dev Sets or unsets the approval of a given operator + * @dev An operator is allowed to transfer all tokens of the sender on their behalf + * @param _to operator address to set the approval + * @param _approved representing the status of the approval to be set + */ + function setApprovalForAll(address _to, bool _approved) + public + { + require(_to != msg.sender); + operatorApprovals[msg.sender][_to] = _approved; + emit ApprovalForAll(msg.sender, _to, _approved); + } + + /** + * @dev Tells whether an operator is approved by a given owner + * @param _owner owner address which you want to query the approval of + * @param _operator operator address which you want to query the approval of + * @return bool whether the given operator is approved by the given owner + */ + function isApprovedForAll(address _owner, address _operator) + public + view + returns (bool) + { + return operatorApprovals[_owner][_operator]; + } + + /** + * @dev Transfers the ownership of a given token ID to another address + * @dev Usage of this method is discouraged, use `safeTransferFrom` whenever possible + * @dev Requires the msg sender to be the owner, approved, or operator + * @param _from current owner of the token + * @param _to address to receive the ownership of the given token ID + * @param _tokenId uint256 ID of the token to be transferred + */ + function transferFrom(address _from, address _to, uint256 _tokenId) + public + canTransfer(_tokenId) + { + require(_from != address(0)); + require(_to != address(0)); + + clearApproval(_from, _tokenId); + removeTokenFrom(_from, _tokenId); + addTokenTo(_to, _tokenId); + + emit Transfer(_from, _to, _tokenId); + } + + /** + * @dev Safely transfers the ownership of a given token ID to another address + * @dev If the target address is a contract, it must implement `onERC721Received`, + * which is called upon a safe transfer, and return the magic value + * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise, + * the transfer is reverted. + * @dev Requires the msg sender to be the owner, approved, or operator + * @param _from current owner of the token + * @param _to address to receive the ownership of the given token ID + * @param _tokenId uint256 ID of the token to be transferred + */ + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId) + public + canTransfer(_tokenId) + { + // solium-disable-next-line arg-overflow + safeTransferFrom(_from, _to, _tokenId, ""); + } + + /** + * @dev Safely transfers the ownership of a given token ID to another address + * @dev If the target address is a contract, it must implement `onERC721Received`, + * which is called upon a safe transfer, and return the magic value + * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise, + * the transfer is reverted. + * @dev Requires the msg sender to be the owner, approved, or operator + * @param _from current owner of the token + * @param _to address to receive the ownership of the given token ID + * @param _tokenId uint256 ID of the token to be transferred + * @param _data bytes data to send along with a safe transfer check + */ + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId, + bytes _data) + public + canTransfer(_tokenId) + { + transferFrom(_from, _to, _tokenId); + // solium-disable-next-line arg-overflow + require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data)); + } + + /** + * @dev Returns whether the given spender can transfer a given token ID + * @param _spender address of the spender to query + * @param _tokenId uint256 ID of the token to be transferred + * @return bool whether the msg.sender is approved for the given token ID, + * is an operator of the owner, or is the owner of the token + */ + function isApprovedOrOwner(address _spender, uint256 _tokenId) + internal + view + returns (bool) + { + address owner = ownerOf(_tokenId); + return _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender); + } + + /** + * @dev Internal function to mint a new token + * @dev Reverts if the given token ID already exists + * @param _to The address that will own the minted token + * @param _tokenId uint256 ID of the token to be minted by the msg.sender + */ + function _mint(address _to, uint256 _tokenId) + internal + { + require(_to != address(0)); + addTokenTo(_to, _tokenId); + emit Transfer(address(0), _to, _tokenId); + } + + /** + * @dev Internal function to burn a specific token + * @dev Reverts if the token does not exist + * @param _tokenId uint256 ID of the token being burned by the msg.sender + */ + function _burn(address _owner, uint256 _tokenId) + internal + { + clearApproval(_owner, _tokenId); + removeTokenFrom(_owner, _tokenId); + emit Transfer(_owner, address(0), _tokenId); + } + + /** + * @dev Internal function to clear current approval of a given token ID + * @dev Reverts if the given address is not indeed the owner of the token + * @param _owner owner of the token + * @param _tokenId uint256 ID of the token to be transferred + */ + function clearApproval(address _owner, uint256 _tokenId) + internal + { + require(ownerOf(_tokenId) == _owner); + if (tokenApprovals[_tokenId] != address(0)) { + tokenApprovals[_tokenId] = address(0); + emit Approval(_owner, address(0), _tokenId); + } + } + + /** + * @dev Internal function to add a token ID to the list of a given address + * @param _to address representing the new owner of the given token ID + * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function addTokenTo(address _to, uint256 _tokenId) + internal + { + require(tokenOwner[_tokenId] == address(0)); + tokenOwner[_tokenId] = _to; + ownedTokensCount[_to] = safeAdd(ownedTokensCount[_to], 1); + } + + /** + * @dev Internal function to remove a token ID from the list of a given address + * @param _from address representing the previous owner of the given token ID + * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function removeTokenFrom(address _from, uint256 _tokenId) + internal + { + require(ownerOf(_tokenId) == _from); + ownedTokensCount[_from] = safeSub(ownedTokensCount[_from], 1); + tokenOwner[_tokenId] = address(0); + } + + /** + * @dev Internal function to invoke `onERC721Received` on a target address + * @dev The call is not executed if the target address is not a contract + * @param _from address representing the previous owner of the given token ID + * @param _to target address that will receive the tokens + * @param _tokenId uint256 ID of the token to be transferred + * @param _data bytes optional data to send along with the call + * @return whether the call correctly returned the expected magic value + */ + function checkAndCallSafeTransfer( + address _from, + address _to, + uint256 _tokenId, + bytes _data) + internal + returns (bool) + { + if (!isContract(_to)) { + return true; + } + bytes4 retval = IERC721Receiver(_to).onERC721Received(_from, _tokenId, _data); + return (retval == ERC721_RECEIVED); + } + + function isContract(address addr) + internal + view + returns (bool) + { + uint256 size; + // XXX Currently there is no better way to check if there is a contract in an address + // than to check the size of the code at that address. + // See https://ethereum.stackexchange.com/a/14016/36603 + // for more details about how this works. + // TODO Check this again before the Serenity release, because all addresses will be + // contracts then. + assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly + return size > 0; + } +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol new file mode 100644 index 000000000..26871cc89 --- /dev/null +++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Receiver.sol @@ -0,0 +1,60 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Smart Contract Solutions, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.21; + +/** + * @title ERC721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * rom ERC721 asset contracts. + * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Receiver.sol + */ +contract IERC721Receiver { + /** + * @dev Magic value to be returned upon successful reception of an NFT + * Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`, + * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector` + */ + bytes4 constant ERC721_RECEIVED = 0xf0b9e5ba; + + /** + * @notice Handle the receipt of an NFT + * @dev The ERC721 smart contract calls this function on the recipient + * after a `safetransfer`. This function MAY throw to revert and reject the + * transfer. This function MUST use 50,000 gas or less. Return of other + * than the magic value MUST result in the transaction being reverted. + * Note: the contract address is always the message sender. + * @param _from The sending address + * @param _tokenId The NFT identifier which is being transfered + * @param _data Additional data with no specified format + * @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))` + */ + function onERC721Received( + address _from, + uint256 _tokenId, + bytes _data) + public + returns (bytes4); +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol new file mode 100644 index 000000000..73741ed53 --- /dev/null +++ b/packages/contracts/src/contracts/current/tokens/ERC721Token/IERC721Token.sol @@ -0,0 +1,105 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Smart Contract Solutions, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +pragma solidity ^0.4.21; + +/** + * @title ERC721 Non-Fungible Token Standard basic interface + * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md + * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Basic.sol + */ +contract IERC721Token { + string internal name_; + string internal symbol_; + + event Transfer( + address indexed _from, + address indexed _to, + uint256 _tokenId + ); + event Approval( + address indexed _owner, + address indexed _approved, + uint256 _tokenId + ); + event ApprovalForAll( + address indexed _owner, + address indexed _operator, + bool _approved + ); + + function name() + public + view + returns (string); + function symbol() + public + view + returns (string); + + function balanceOf(address _owner) + public + view + returns (uint256 _balance); + function ownerOf(uint256 _tokenId) + public + view + returns (address _owner); + function exists(uint256 _tokenId) + public + view + returns (bool _exists); + + function approve(address _to, uint256 _tokenId) + public; + function getApproved(uint256 _tokenId) + public + view + returns (address _operator); + + function setApprovalForAll(address _operator, bool _approved) + public; + function isApprovedForAll(address _owner, address _operator) + public + view + returns (bool); + + function transferFrom( + address _from, + address _to, + uint256 _tokenId) + public; + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId) + public; + function safeTransferFrom( + address _from, + address _to, + uint256 _tokenId, + bytes _data) + public; +}
\ No newline at end of file diff --git a/packages/contracts/src/contracts/current/protocol/TokenRegistry/ITokenRegistery.sol b/packages/contracts/src/contracts/previous/TokenRegistry/ITokenRegistery.sol index c140a5f24..b8bdaf3b9 100644 --- a/packages/contracts/src/contracts/current/protocol/TokenRegistry/ITokenRegistery.sol +++ b/packages/contracts/src/contracts/previous/TokenRegistry/ITokenRegistery.sol @@ -18,7 +18,7 @@ pragma solidity ^0.4.21; -import { IOwnable_v1 as IOwnable } from "../../../previous/Ownable/IOwnable_v1.sol"; +import { IOwnable_v1 as IOwnable } from "../Ownable/IOwnable_v1.sol"; /// @title Token Registry - Stores metadata associated with ERC20 tokens. See ERC22 https://github.com/ethereum/EIPs/issues/22 /// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com> diff --git a/packages/contracts/src/contracts/current/protocol/TokenRegistry/TokenRegistry.sol b/packages/contracts/src/contracts/previous/TokenRegistry/TokenRegistry.sol index ede7c8c93..7417a10a3 100644 --- a/packages/contracts/src/contracts/current/protocol/TokenRegistry/TokenRegistry.sol +++ b/packages/contracts/src/contracts/previous/TokenRegistry/TokenRegistry.sol @@ -18,7 +18,7 @@ pragma solidity ^0.4.11; -import { Ownable_v1 as Ownable } from "../../../previous/Ownable/Ownable_v1.sol"; +import { Ownable_v1 as Ownable } from "../Ownable/Ownable_v1.sol"; /// @title Token Registry - Stores metadata associated with ERC20 tokens. See ERC22 https://github.com/ethereum/EIPs/issues/22 /// @author Amir Bandeali - <amir@0xProject.com>, Will Warren - <will@0xProject.com> diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 000905854..27fdd698f 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -221,37 +221,6 @@ export class ExchangeWrapper { const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); return tx; } - public async getOrderHashAsync(signedOrder: SignedOrder): Promise<string> { - const order = orderUtils.getOrderStruct(signedOrder); - const orderHash = await this._exchange.getOrderHash.callAsync(order); - return orderHash; - } - public async isValidSignatureAsync(signedOrder: SignedOrder): Promise<boolean> { - const isValidSignature = await this._exchange.isValidSignature.callAsync( - orderUtils.getOrderHashHex(signedOrder), - signedOrder.makerAddress, - signedOrder.signature, - ); - return isValidSignature; - } - public async isRoundingErrorAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<boolean> { - const isRoundingError = await this._exchange.isRoundingError.callAsync(numerator, denominator, target); - return isRoundingError; - } - public async getPartialAmountAsync( - numerator: BigNumber, - denominator: BigNumber, - target: BigNumber, - ): Promise<BigNumber> { - const partialAmount = new BigNumber( - await this._exchange.getPartialAmount.callAsync(numerator, denominator, target), - ); - return partialAmount; - } public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex)); return filledAmount; diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 934dc52fa..6f9aeda94 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -94,6 +94,8 @@ export enum ContractName { EtherDelta = 'EtherDelta', Arbitrage = 'Arbitrage', TestAssetProxyDispatcher = 'TestAssetProxyDispatcher', + TestLibs = 'TestLibs', + TestSignatureValidator = 'TestSignatureValidator', ERC20Proxy = 'ERC20Proxy', ERC721Proxy = 'ERC721Proxy', DummyERC721Token = 'DummyERC721Token', diff --git a/packages/contracts/test/exchange/helpers.ts b/packages/contracts/test/exchange/helpers.ts deleted file mode 100644 index f986665ff..000000000 --- a/packages/contracts/test/exchange/helpers.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { ZeroEx } from '0x.js'; -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import ethUtil = require('ethereumjs-util'); - -import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; -import { addressUtils } from '../../src/utils/address_utils'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; -import { constants } from '../../src/utils/constants'; -import { ExchangeWrapper } from '../../src/utils/exchange_wrapper'; -import { OrderFactory } from '../../src/utils/order_factory'; -import { orderUtils } from '../../src/utils/order_utils'; -import { AssetProxyId, ContractName, SignedOrder } from '../../src/utils/types'; -import { chaiSetup } from '../utils/chai_setup'; -import { deployer } from '../utils/deployer'; -import { provider, web3Wrapper } from '../utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Exchange helpers', () => { - let signedOrder: SignedOrder; - let exchangeWrapper: ExchangeWrapper; - let orderFactory: OrderFactory; - - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const makerAddress = accounts[0]; - const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [AssetProxyId.ERC20]); - const exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider); - const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); - exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); - - const defaultOrderParams = { - ...constants.STATIC_ORDER_PARAMS, - exchangeAddress: exchange.address, - makerAddress, - feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), - makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), - }; - const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - orderFactory = new OrderFactory(privateKey, defaultOrderParams); - }); - - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - signedOrder = orderFactory.newSignedOrder(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('getOrderHash', () => { - it('should output the correct orderHash', async () => { - const orderHashHex = await exchangeWrapper.getOrderHashAsync(signedOrder); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); - }); - }); - - describe('isValidSignature', () => { - beforeEach(async () => { - signedOrder = orderFactory.newSignedOrder(); - }); - - it('should return true with a valid signature', async () => { - const success = await exchangeWrapper.isValidSignatureAsync(signedOrder); - expect(success).to.be.true(); - }); - - it('should return false with an invalid signature', async () => { - const invalidR = ethUtil.sha3('invalidR'); - const invalidS = ethUtil.sha3('invalidS'); - const invalidSigBuff = Buffer.concat([ - ethUtil.toBuffer(signedOrder.signature.slice(0, 6)), - invalidR, - invalidS, - ]); - const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; - signedOrder.signature = invalidSigHex; - const success = await exchangeWrapper.isValidSignatureAsync(signedOrder); - expect(success).to.be.false(); - }); - }); - - describe('isRoundingError', () => { - it('should return false if there is a rounding error of 0.1%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(999); - const target = new BigNumber(50); - // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return false if there is a rounding of 0.09%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9991); - const target = new BigNumber(500); - // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return true if there is a rounding error of 0.11%', async () => { - const numerator = new BigNumber(20); - const denominator = new BigNumber(9989); - const target = new BigNumber(500); - // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); - }); - - it('should return true if there is a rounding error > 0.1%', async () => { - const numerator = new BigNumber(3); - const denominator = new BigNumber(7); - const target = new BigNumber(10); - // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.true(); - }); - - it('should return false when there is no rounding error', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(2); - const target = new BigNumber(10); - - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - - it('should return false when there is rounding error <= 0.1%', async () => { - // randomly generated numbers - const numerator = new BigNumber(76564); - const denominator = new BigNumber(676373677); - const target = new BigNumber(105762562); - // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / - // (76564*105762562/676373677) = 0.0007% - const isRoundingError = await exchangeWrapper.isRoundingErrorAsync(numerator, denominator, target); - expect(isRoundingError).to.be.false(); - }); - }); - - describe('getPartialAmount', () => { - it('should return the numerator/denominator*target', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(2); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 5; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - - it('should round down', async () => { - const numerator = new BigNumber(2); - const denominator = new BigNumber(3); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 6; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - - it('should round .5 down', async () => { - const numerator = new BigNumber(1); - const denominator = new BigNumber(20); - const target = new BigNumber(10); - - const partialAmount = await exchangeWrapper.getPartialAmountAsync(numerator, denominator, target); - const expectedPartialAmount = 0; - expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); - }); - }); -}); diff --git a/packages/contracts/test/exchange/libs.ts b/packages/contracts/test/exchange/libs.ts new file mode 100644 index 000000000..0074cdff7 --- /dev/null +++ b/packages/contracts/test/exchange/libs.ts @@ -0,0 +1,155 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import ethUtil = require('ethereumjs-util'); + +import { TestLibsContract } from '../../src/contract_wrappers/generated/test_libs'; +import { addressUtils } from '../../src/utils/address_utils'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { OrderFactory } from '../../src/utils/order_factory'; +import { orderUtils } from '../../src/utils/order_utils'; +import { ContractName, SignedOrder } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Exchange libs', () => { + let signedOrder: SignedOrder; + let orderFactory: OrderFactory; + let libs: TestLibsContract; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const makerAddress = accounts[0]; + const libsInstance = await deployer.deployAsync(ContractName.TestLibs); + libs = new TestLibsContract(libsInstance.abi, libsInstance.address, provider); + const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); + + const defaultOrderParams = { + ...constants.STATIC_ORDER_PARAMS, + exchangeAddress: libs.address, + makerAddress, + feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), + makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + }; + const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + }); + + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + signedOrder = orderFactory.newSignedOrder(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('LibOrder', () => { + describe('getOrderHash', () => { + it('should output the correct orderHash', async () => { + const orderHashHex = await libs.publicGetOrderHash.callAsync(signedOrder); + expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex); + }); + }); + }); + + describe('LibMath', () => { + describe('isRoundingError', () => { + it('should return false if there is a rounding error of 0.1%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(999); + const target = new BigNumber(50); + // rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false if there is a rounding of 0.09%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9991); + const target = new BigNumber(500); + // rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return true if there is a rounding error of 0.11%', async () => { + const numerator = new BigNumber(20); + const denominator = new BigNumber(9989); + const target = new BigNumber(500); + // rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return true if there is a rounding error > 0.1%', async () => { + const numerator = new BigNumber(3); + const denominator = new BigNumber(7); + const target = new BigNumber(10); + // rounding error = ((3*10/7) - floor(3*10/7)) / (3*10/7) = 6.67% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.true(); + }); + + it('should return false when there is no rounding error', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(2); + const target = new BigNumber(10); + + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + + it('should return false when there is rounding error <= 0.1%', async () => { + // randomly generated numbers + const numerator = new BigNumber(76564); + const denominator = new BigNumber(676373677); + const target = new BigNumber(105762562); + // rounding error = ((76564*105762562/676373677) - floor(76564*105762562/676373677)) / + // (76564*105762562/676373677) = 0.0007% + const isRoundingError = await libs.publicIsRoundingError.callAsync(numerator, denominator, target); + expect(isRoundingError).to.be.false(); + }); + }); + + describe('getPartialAmount', () => { + it('should return the numerator/denominator*target', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(2); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 5; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + + it('should round down', async () => { + const numerator = new BigNumber(2); + const denominator = new BigNumber(3); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 6; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + + it('should round .5 down', async () => { + const numerator = new BigNumber(1); + const denominator = new BigNumber(20); + const target = new BigNumber(10); + + const partialAmount = await libs.publicGetPartialAmount.callAsync(numerator, denominator, target); + const expectedPartialAmount = 0; + expect(partialAmount).to.be.bignumber.equal(expectedPartialAmount); + }); + }); + }); +}); diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts new file mode 100644 index 000000000..f47999bfa --- /dev/null +++ b/packages/contracts/test/exchange/signature_validator.ts @@ -0,0 +1,93 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import ethUtil = require('ethereumjs-util'); + +import { TestSignatureValidatorContract } from '../../src/contract_wrappers/generated/test_signature_validator'; +import { addressUtils } from '../../src/utils/address_utils'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { OrderFactory } from '../../src/utils/order_factory'; +import { orderUtils } from '../../src/utils/order_utils'; +import { ContractName, SignedOrder } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('MixinSignatureValidator', () => { + let signedOrder: SignedOrder; + let orderFactory: OrderFactory; + let signatureValidator: TestSignatureValidatorContract; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const makerAddress = accounts[0]; + const signatureValidatorInstance = await deployer.deployAsync(ContractName.TestSignatureValidator); + signatureValidator = new TestSignatureValidatorContract( + signatureValidatorInstance.abi, + signatureValidatorInstance.address, + provider, + ); + const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); + + const defaultOrderParams = { + ...constants.STATIC_ORDER_PARAMS, + exchangeAddress: signatureValidatorInstance.address, + makerAddress, + feeRecipientAddress: addressUtils.generatePseudoRandomAddress(), + makerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + takerAssetData: assetProxyUtils.encodeERC20ProxyData(addressUtils.generatePseudoRandomAddress()), + }; + const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; + orderFactory = new OrderFactory(privateKey, defaultOrderParams); + }); + + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + signedOrder = orderFactory.newSignedOrder(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('isValidSignature', () => { + beforeEach(async () => { + signedOrder = orderFactory.newSignedOrder(); + }); + + it('should return true with a valid signature', async () => { + const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const success = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + signedOrder.makerAddress, + signedOrder.signature, + ); + expect(success).to.be.true(); + }); + + it('should return false with an invalid signature', async () => { + const invalidR = ethUtil.sha3('invalidR'); + const invalidS = ethUtil.sha3('invalidS'); + const invalidSigBuff = Buffer.concat([ + ethUtil.toBuffer(signedOrder.signature.slice(0, 6)), + invalidR, + invalidS, + ]); + const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`; + signedOrder.signature = invalidSigHex; + const orderHashHex = orderUtils.getOrderHashHex(signedOrder); + const success = await signatureValidator.publicIsValidSignature.callAsync( + orderHashHex, + signedOrder.makerAddress, + signedOrder.signature, + ); + expect(success).to.be.false(); + }); + }); +}); |