From fcc21c8a4d9a80c9d1731a06ddc43e6840b40d7e Mon Sep 17 00:00:00 2001 From: felix2feng Date: Tue, 3 Jul 2018 16:52:22 -0700 Subject: [PATCH] Add 0x interfaces Import interfacesD Blah Need to get to the bottom of this Finish parsing maker data Remove all external from solcoverage --- .solcover.js | 2 +- .../ZeroExExchangeWrapper.sol | 107 +++++++++++++ .../lib/ZeroExOrderDataHandler.sol | 59 ++++--- .../core/external/ZeroExExchangeWrapper.sol | 85 ---------- contracts/core/lib/ERC20Wrapper.sol | 18 +++ .../0x/AssetProxy/interfaces/IAssetData.sol | 36 +++++ .../0x/AssetProxy/interfaces/IAssetProxy.sol | 47 ++++++ .../AssetProxy/interfaces/IAuthorizable.sol | 63 ++++++++ .../AssetProxy/libs/LibAssetProxyErrors.sol | 36 +++++ .../interfaces/IAssetProxyDispatcher.sol | 42 +++++ .../0x/Exchange/interfaces/IExchange.sol | 36 +++++ .../0x/Exchange/interfaces/IExchangeCore.sol | 59 +++++++ .../0x/Exchange/interfaces/IMatchOrders.sol | 44 +++++ .../interfaces/ISignatureValidator.sol | 56 +++++++ .../0x/Exchange/interfaces/ITransactions.sol | 34 ++++ .../0x/Exchange/interfaces/IValidator.sol | 36 +++++ .../0x/Exchange/interfaces/IWallet.sol | 34 ++++ .../Exchange/interfaces/IWrapperFunctions.sol | 150 ++++++++++++++++++ .../0x/Exchange/libs/LibConstants.sol | 27 ++++ .../external/0x/Exchange/libs/LibEIP712.sol | 42 +++++ .../0x/Exchange/libs/LibExchangeErrors.sol | 69 ++++++++ .../0x/Exchange/libs/LibFillResults.sol | 35 ++++ .../external/0x/Exchange/libs/LibOrder.sol | 77 +++++++++ contracts/external/LibBytes.sol | 24 +++ contracts/lib/CommonMath.sol | 35 ++++ ... => MockZeroExOrderDataHandlerLibrary.sol} | 20 ++- .../mockZeroExOrderDataHandlerLibrary.spec.ts | 42 ++++- .../external/zeroExExchangeWrapper.spec.ts | 5 + test/utils/zeroExExchangeWrapper.ts | 20 ++- 29 files changed, 1217 insertions(+), 123 deletions(-) create mode 100644 contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol rename contracts/core/{external => exchange-wrappers}/lib/ZeroExOrderDataHandler.sol (82%) delete mode 100644 contracts/core/external/ZeroExExchangeWrapper.sol create mode 100644 contracts/external/0x/AssetProxy/interfaces/IAssetData.sol create mode 100644 contracts/external/0x/AssetProxy/interfaces/IAssetProxy.sol create mode 100644 contracts/external/0x/AssetProxy/interfaces/IAuthorizable.sol create mode 100644 contracts/external/0x/AssetProxy/libs/LibAssetProxyErrors.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IAssetProxyDispatcher.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IExchange.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IExchangeCore.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IMatchOrders.sol create mode 100644 contracts/external/0x/Exchange/interfaces/ISignatureValidator.sol create mode 100644 contracts/external/0x/Exchange/interfaces/ITransactions.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IValidator.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IWallet.sol create mode 100644 contracts/external/0x/Exchange/interfaces/IWrapperFunctions.sol create mode 100644 contracts/external/0x/Exchange/libs/LibConstants.sol create mode 100644 contracts/external/0x/Exchange/libs/LibEIP712.sol create mode 100644 contracts/external/0x/Exchange/libs/LibExchangeErrors.sol create mode 100644 contracts/external/0x/Exchange/libs/LibFillResults.sol create mode 100644 contracts/external/0x/Exchange/libs/LibOrder.sol create mode 100644 contracts/lib/CommonMath.sol rename contracts/test/lib/{ZeroExOrderDataHandlerLib.sol => MockZeroExOrderDataHandlerLibrary.sol} (70%) diff --git a/.solcover.js b/.solcover.js index e753093f7..94ce572e0 100644 --- a/.solcover.js +++ b/.solcover.js @@ -4,7 +4,7 @@ module.exports = { skipFiles: [ 'Migrations.sol', 'test', - 'external/LibBytes.sol', + 'external', 'core/lib/ERC20Wrapper.sol' // TODO: Find a cleaner way to test state mutating functions, we will skip ], }; diff --git a/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol b/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol new file mode 100644 index 000000000..494640ba6 --- /dev/null +++ b/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol @@ -0,0 +1,107 @@ +/* + Copyright 2018 Set Labs Inc. + + 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 { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; +import { ZeroExOrderDataHandler as OrderHandler } from "./lib/ZeroExOrderDataHandler.sol"; +import { LibBytes } from "../../external/LibBytes.sol"; +import { LibOrder as ZeroExOrder } from "../../external/0x/Exchange/libs/LibOrder.sol"; +import { LibFillResults as ZeroExFillResults } from "../../external/0x/Exchange/libs/LibFillResults.sol"; +import { IExchange as ZeroExExchange } from "../../external/0x/Exchange/interfaces/IExchange.sol"; +import { ERC20Wrapper as ERC20 } from "../../core/lib/ERC20Wrapper.sol"; + + + +/** + * @title ZeroExExchangeWrapper + * @author Set Protocol + * + * The ZeroExExchangeWrapper contract wrapper to interface with 0x V2 + */ +contract ZeroExExchangeWrapper +{ + using SafeMath for uint256; + + /* ============ State Variables ============ */ + + address public ZERO_EX_EXCHANGE; + address public ZERO_EX_PROXY; + address public SET_PROXY; + + + /* ============ Constructor ============ */ + + constructor( + address _zeroExExchange, + address _zeroExProxy, + address _setProxy + ) + public + { + ZERO_EX_EXCHANGE = _zeroExExchange; + ZERO_EX_PROXY = _zeroExProxy; + SET_PROXY = _setProxy; + } + + + /* ============ Public Functions ============ */ + + + // The purpose of this function is to decode the order data and execute the trade + // TODO - We are currently assuming no taker fee. Add in taker fee going forward + function exchange( + address _tradeOriginator, + bytes _orderData + ) + external + // returns (uint256) + { + // Loop through order data and perform each order + + // Approve the taker token for transfer to the Set Vault + + + // return 1; + } + + /* ============ Getters ============ */ + + /* ============ Private ============ */ + function fillZeroExOrder( + bytes _zeroExOrderData + ) + private + returns (ZeroExFillResults.FillResults memory) + { + uint256 fillAmount = OrderHandler.parseFillAmount(_zeroExOrderData); + bytes memory signature = OrderHandler.sliceSignature(_zeroExOrderData); + ZeroExOrder.Order memory order = OrderHandler.parseZeroExOrder(_zeroExOrderData); + + // Ensure the maker token is allowed to be approved to the ZeroEx proxy + + + ZeroExFillResults.FillResults memory fillResults = + ZeroExExchange(ZERO_EX_EXCHANGE).fillOrKillOrder( + order, + fillAmount, + signature + ); + + // Ensure the taker token is allowed to be approved to the TransferProxy + } +} diff --git a/contracts/core/external/lib/ZeroExOrderDataHandler.sol b/contracts/core/exchange-wrappers/lib/ZeroExOrderDataHandler.sol similarity index 82% rename from contracts/core/external/lib/ZeroExOrderDataHandler.sol rename to contracts/core/exchange-wrappers/lib/ZeroExOrderDataHandler.sol index ad08379f9..e6318d843 100644 --- a/contracts/core/external/lib/ZeroExOrderDataHandler.sol +++ b/contracts/core/exchange-wrappers/lib/ZeroExOrderDataHandler.sol @@ -19,6 +19,7 @@ pragma experimental "ABIEncoderV2"; import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; import { LibBytes } from "../../../external/LibBytes.sol"; +import { LibOrder } from "../../../external/0x/Exchange/libs/LibOrder.sol"; /** @@ -33,21 +34,6 @@ library ZeroExOrderDataHandler { // ============ Structs ============ - struct Order { - address makerAddress; // Address that created the order. - address takerAddress; // Address that is allowed to fill the order. - address feeRecipientAddress; // Address that will recieve fees when order is filled. - address senderAddress; // Address that is allowed to call Exchange contract. - uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. - uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. - uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker - uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker - uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. - uint256 salt; // Number to facilitate uniqueness of the order's hash. - bytes makerAssetData; // Encoded data when transferring makerAsset. - bytes takerAssetData; // Encoded data when transferring takerAsset. - } - struct ZeroExHeader { uint256 signatureLength; uint256 orderLength; @@ -55,6 +41,11 @@ library ZeroExOrderDataHandler { uint256 takerAssetDataLength; } + struct AssetDataAddresses { + address makerTokenAddress; + address takerTokenAddress; + } + // ============ Internal Functions ============ // We construct the following to allow calling fillOrder on ZeroEx V2 Exchange @@ -111,12 +102,18 @@ library ZeroExOrderDataHandler { return fillAmount; } - function sliceSignature(bytes _orderData, uint _signatureLength) + function sliceSignature(bytes _orderData) internal pure returns (bytes) { - bytes memory signature = _orderData.slice(160, _signatureLength.add(160)); + uint256 orderDataAddr = _orderData.contentAddress(); + uint256 signatureLength; + assembly { + signatureLength := mload(orderDataAddr) + } + + bytes memory signature = _orderData.slice(160, signatureLength.add(160)); return signature; } @@ -134,16 +131,16 @@ library ZeroExOrderDataHandler { return order; } - function parseZeroExOrder( + function constructZeroExOrder( bytes _zeroExOrder, uint _makerAssetDataLength, uint _takerAssetDataLength ) internal pure - returns (Order memory) + returns (LibOrder.Order memory) { - Order memory order; + LibOrder.Order memory order; uint256 orderDataAddr = _zeroExOrder.contentAddress(); // | Data | Location | Length | @@ -184,14 +181,14 @@ library ZeroExOrderDataHandler { return order; } - function parseZeroExOrderData(bytes _orderData) + function parseZeroExOrder(bytes _orderData) internal pure - returns(Order memory) + returns(LibOrder.Order memory) { ZeroExHeader memory header = parseOrderHeader(_orderData); - Order memory order = parseZeroExOrder( + LibOrder.Order memory order = constructZeroExOrder( sliceZeroExOrder(_orderData, header.signatureLength, header.orderLength), header.makerAssetDataLength, header.takerAssetDataLength @@ -199,4 +196,20 @@ library ZeroExOrderDataHandler { return order; } + + function parseERC20TokenAddress(bytes _assetData) + internal + pure + returns(address) + { + bytes4 ERC20_SELECTOR = bytes4(keccak256("ERC20Token(address)")); + // Ensure that the asset is ERC20 + bytes4 orderProxyId = _assetData.readBytes4(0); + + require(ERC20_SELECTOR == orderProxyId); + + address tokenAddress = address(_assetData.readBytes32(4)); + + return tokenAddress; + } } diff --git a/contracts/core/external/ZeroExExchangeWrapper.sol b/contracts/core/external/ZeroExExchangeWrapper.sol deleted file mode 100644 index 904949606..000000000 --- a/contracts/core/external/ZeroExExchangeWrapper.sol +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2018 Set Labs Inc. - - 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 { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; -import { LibBytes } from "../../external/LibBytes.sol"; -import { ZeroExOrderDataHandler as ZeroEx } from "./lib/ZeroExOrderDataHandler.sol"; - - -/** - * @title ZeroExExchangeWrapper - * @author Set Protocol - * - * The ZeroExExchangeWrapper contract wrapper to interface with 0x V2 - */ -contract ZeroExExchangeWrapper -{ - using SafeMath for uint256; - - /* ============ State Variables ============ */ - - address public ZERO_EX_EXCHANGE; - address public ZERO_EX_PROXY; - - - /* ============ Constructor ============ */ - - constructor( - // address _zeroExExchange, - // address _zeroExProxy, - ) - public - { - // ZERO_EX_EXCHANGE = _zeroExExchange; - // ZERO_EX_PROXY = _zeroExProxy; - } - - - /* ============ Public Functions ============ */ - - function exchange( - address tradeOriginator, - bytes orderData - ) - external - // returns (uint256) - { - - // Parse fill Amount - // uint256 fillAmount = parseFillAmount(_orderData); - - // Slice the signature out. - - // Slice the Order - - // Construct the order - // Order memory order = parseZeroExOrder(orderData); - - // Move the required takerToken into the wrapper - - // Ensure allowance - - - // return 1; - } - - /* ============ Getters ============ */ - - /* ============ Private ============ */ -} diff --git a/contracts/core/lib/ERC20Wrapper.sol b/contracts/core/lib/ERC20Wrapper.sol index ecd4f8a0a..41557ab58 100644 --- a/contracts/core/lib/ERC20Wrapper.sol +++ b/contracts/core/lib/ERC20Wrapper.sol @@ -17,6 +17,7 @@ pragma solidity 0.4.24; import { IERC20 } from "../../lib/IERC20.sol"; +import { CommonMath } from "../../lib/CommonMath.sol"; /** @@ -106,6 +107,23 @@ library ERC20Wrapper { ); } + function ensureAllowance( + address _token, + address _owner, + address _spender, + uint256 _quantity + ) + private + { + if (allowance(_token, _owner, _spender) < _quantity) { + approve( + _token, + _spender, + CommonMath.maxUInt256() + ); + } + } + // ============ Private Functions ============ /** diff --git a/contracts/external/0x/AssetProxy/interfaces/IAssetData.sol b/contracts/external/0x/AssetProxy/interfaces/IAssetData.sol new file mode 100644 index 000000000..8c78ee8c4 --- /dev/null +++ b/contracts/external/0x/AssetProxy/interfaces/IAssetData.sol @@ -0,0 +1,36 @@ +/* + + 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.23; + +// @dev Interface of the asset proxy's assetData. +// The asset proxies take an ABI encoded `bytes assetData` as argument. +// This argument is ABI encoded as one of the methods of this interface. +interface IAssetData { + + function ERC20Token( + address tokenContract) + external pure; + + function ERC721Token( + address tokenContract, + uint256 tokenId, + bytes receiverData) + external pure; + +} diff --git a/contracts/external/0x/AssetProxy/interfaces/IAssetProxy.sol b/contracts/external/0x/AssetProxy/interfaces/IAssetProxy.sol new file mode 100644 index 000000000..ae8e195da --- /dev/null +++ b/contracts/external/0x/AssetProxy/interfaces/IAssetProxy.sol @@ -0,0 +1,47 @@ +/* + + 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 "./IAuthorizable.sol"; + +contract IAssetProxy is + IAuthorizable +{ + + /// @dev Transfers assets. Either succeeds or throws. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param from Address to transfer asset from. + /// @param to Address to transfer asset to. + /// @param amount Amount of asset to transfer. + function transferFrom( + bytes assetData, + address from, + address to, + uint256 amount + ) + external; + + /// @dev Gets the proxy id associated with the proxy address. + /// @return Proxy id. + function getProxyId() + external + view + returns (bytes4); +} diff --git a/contracts/external/0x/AssetProxy/interfaces/IAuthorizable.sol b/contracts/external/0x/AssetProxy/interfaces/IAuthorizable.sol new file mode 100644 index 000000000..a78ff73d9 --- /dev/null +++ b/contracts/external/0x/AssetProxy/interfaces/IAuthorizable.sol @@ -0,0 +1,63 @@ +/* + + 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; + +/* + * Ownable + * + * Base contract with an owner. + * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. + */ + +contract IOwnable { + function transferOwnership(address newOwner) + public; +} + +contract IAuthorizable is + IOwnable +{ + + /// @dev Gets all authorized addresses. + /// @return Array of authorized addresses. + function getAuthorizedAddresses() + external + view + returns (address[]); + + /// @dev Authorizes an address. + /// @param target Address to authorize. + function addAuthorizedAddress(address target) + external; + + /// @dev Removes authorizion of an address. + /// @param target Address to remove authorization from. + function removeAuthorizedAddress(address target) + external; + + /// @dev Removes authorizion of an address. + /// @param target Address to remove authorization from. + /// @param index Index of target in authorities array. + function removeAuthorizedAddressAtIndex( + address target, + uint256 index + ) + external; +} diff --git a/contracts/external/0x/AssetProxy/libs/LibAssetProxyErrors.sol b/contracts/external/0x/AssetProxy/libs/LibAssetProxyErrors.sol new file mode 100644 index 000000000..338cb12e2 --- /dev/null +++ b/contracts/external/0x/AssetProxy/libs/LibAssetProxyErrors.sol @@ -0,0 +1,36 @@ +/* + + 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; + +/// @dev This contract documents the revert reasons used in the AssetProxy contracts. +/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons. +contract LibAssetProxyErrors { + + /// Authorizable errors /// + string constant SENDER_NOT_AUTHORIZED = "SENDER_NOT_AUTHORIZED"; // Sender not authorized to call this method. + string constant TARGET_NOT_AUTHORIZED = "TARGET_NOT_AUTHORIZED"; // Target address not authorized to call this method. + string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized. + string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds. + string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address. + + /// Transfer errors /// + string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1. + string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed. + string constant LENGTH_GREATER_THAN_131_REQUIRED = "LENGTH_GREATER_THAN_131_REQUIRED"; // Byte array must have a length greater than 0. +} diff --git a/contracts/external/0x/Exchange/interfaces/IAssetProxyDispatcher.sol b/contracts/external/0x/Exchange/interfaces/IAssetProxyDispatcher.sol new file mode 100644 index 000000000..fa55dff00 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IAssetProxyDispatcher.sol @@ -0,0 +1,42 @@ +/* + + 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 IAssetProxyDispatcher { + + /// @dev Registers an asset proxy to an asset proxy id. + /// An id can only be assigned to a single proxy at a given time. + /// @param assetProxyId Id to register`newAssetProxy` under. + /// @param newAssetProxy Address of new asset proxy to register, or 0x0 to unset assetProxyId. + /// @param oldAssetProxy Existing asset proxy to overwrite, or 0x0 if assetProxyId is currently unused. + function registerAssetProxy( + bytes4 assetProxyId, + address newAssetProxy, + address oldAssetProxy + ) + external; + + /// @dev Gets an asset proxy. + /// @param assetProxyId Id of the asset proxy. + /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered. + function getAssetProxy(bytes4 assetProxyId) + external + view + returns (address); +} diff --git a/contracts/external/0x/Exchange/interfaces/IExchange.sol b/contracts/external/0x/Exchange/interfaces/IExchange.sol new file mode 100644 index 000000000..9f21c18d7 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IExchange.sol @@ -0,0 +1,36 @@ +/* + + 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 "./IExchangeCore.sol"; +import "./IMatchOrders.sol"; +import "./ISignatureValidator.sol"; +import "./ITransactions.sol"; +import "./IAssetProxyDispatcher.sol"; +import "./IWrapperFunctions.sol"; + +contract IExchange is + IExchangeCore, + IMatchOrders, + ISignatureValidator, + ITransactions, + IAssetProxyDispatcher, + IWrapperFunctions +{} diff --git a/contracts/external/0x/Exchange/interfaces/IExchangeCore.sol b/contracts/external/0x/Exchange/interfaces/IExchangeCore.sol new file mode 100644 index 000000000..98222f33f --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IExchangeCore.sol @@ -0,0 +1,59 @@ +/* + + 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/LibOrder.sol"; +import "../libs/LibFillResults.sol"; + +contract IExchangeCore { + + /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch + /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). + /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled. + function cancelOrdersUpTo(uint256 targetOrderEpoch) + external; + + /// @dev Fills the input order. + /// @param order Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrder( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev After calling, the order can not be filled anymore. + /// @param order Order struct containing order specifications. + function cancelOrder(LibOrder.Order memory order) + public; + + /// @dev Gets information about an order: status, hash, and amount filled. + /// @param order Order to gather information on. + /// @return OrderInfo Information about the order and its state. + /// See LibOrder.OrderInfo for a complete description. + function getOrderInfo(LibOrder.Order memory order) + public + view + returns (LibOrder.OrderInfo memory orderInfo); +} diff --git a/contracts/external/0x/Exchange/interfaces/IMatchOrders.sol b/contracts/external/0x/Exchange/interfaces/IMatchOrders.sol new file mode 100644 index 000000000..df009d063 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IMatchOrders.sol @@ -0,0 +1,44 @@ +/* + + 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/LibOrder.sol"; +import "../libs/LibFillResults.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 goes to the taker (who matched the two orders). + /// @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. + /// @return matchedFillResults Amounts filled and fees paid by maker and taker of matched orders. + /// TODO: Make this function external once supported by Solidity (See Solidity Issues #3199, #1603) + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + returns (LibFillResults.MatchedFillResults memory matchedFillResults); +} diff --git a/contracts/external/0x/Exchange/interfaces/ISignatureValidator.sol b/contracts/external/0x/Exchange/interfaces/ISignatureValidator.sol new file mode 100644 index 000000000..511463309 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/ISignatureValidator.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; + +contract ISignatureValidator { + + /// @dev Approves a hash on-chain using any valid signature type. + /// After presigning a hash, the preSign signature type will become valid for that hash and signer. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof that the hash has been signed by signer. + function preSign( + bytes32 hash, + address signerAddress, + bytes signature + ) + external; + + /// @dev Approves/unnapproves a Validator contract to verify signatures on signer's behalf. + /// @param validatorAddress Address of Validator contract. + /// @param approval Approval or disapproval of Validator contract. + function setSignatureValidatorApproval( + address validatorAddress, + bool approval + ) + external; + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signerAddress Address of signer. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes memory signature + ) + public + view + returns (bool isValid); +} diff --git a/contracts/external/0x/Exchange/interfaces/ITransactions.sol b/contracts/external/0x/Exchange/interfaces/ITransactions.sol new file mode 100644 index 000000000..a7cab8f55 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/ITransactions.sol @@ -0,0 +1,34 @@ +/* + + 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 ITransactions { + + /// @dev Executes an exchange method call in the context of signer. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signerAddress Address of transaction signer. + /// @param data AbiV2 encoded calldata. + /// @param signature Proof of signer transaction by signer. + function executeTransaction( + uint256 salt, + address signerAddress, + bytes data, + bytes signature + ) + external; +} diff --git a/contracts/external/0x/Exchange/interfaces/IValidator.sol b/contracts/external/0x/Exchange/interfaces/IValidator.sol new file mode 100644 index 000000000..0b1796a66 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IValidator.sol @@ -0,0 +1,36 @@ +/* + + 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.23; + +contract IValidator { + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signerAddress Address that should have signed the given hash. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + address signerAddress, + bytes signature + ) + external + view + returns (bool isValid); +} diff --git a/contracts/external/0x/Exchange/interfaces/IWallet.sol b/contracts/external/0x/Exchange/interfaces/IWallet.sol new file mode 100644 index 000000000..c86a2c057 --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IWallet.sol @@ -0,0 +1,34 @@ +/* + + 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 IWallet { + + /// @dev Verifies that a signature is valid. + /// @param hash Message hash that is signed. + /// @param signature Proof of signing. + /// @return Validity of order signature. + function isValidSignature( + bytes32 hash, + bytes signature + ) + external + view + returns (bool isValid); +} diff --git a/contracts/external/0x/Exchange/interfaces/IWrapperFunctions.sol b/contracts/external/0x/Exchange/interfaces/IWrapperFunctions.sol new file mode 100644 index 000000000..84bb683bc --- /dev/null +++ b/contracts/external/0x/Exchange/interfaces/IWrapperFunctions.sol @@ -0,0 +1,150 @@ +/* + + 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/LibOrder.sol"; +import "../libs/LibFillResults.sol"; + +contract IWrapperFunctions { + /// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled. + /// @param order LibOrder.Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + function fillOrKillOrder( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev Fills an order with specified parameters and ECDSA signature. + /// Returns false if the transaction would otherwise revert. + /// @param order LibOrder.Order struct containing order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signature Proof that order has been created by maker. + /// @return Amounts filled and fees paid by maker and taker. + function fillOrderNoThrow( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + public + returns (LibFillResults.FillResults memory fillResults); + + /// @dev Synchronously executes multiple calls of fillOrder. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrders( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrKill. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrKillOrders( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Fills an order with specified parameters and ECDSA signature. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function batchFillOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256[] memory takerAssetFillAmounts, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellOrders( + LibOrder.Order[] memory orders, + uint256 takerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param takerAssetFillAmount Desired amount of takerAsset to sell. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256 takerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of makerAsset is bought by taker. + /// @param orders Array of order specifications. + /// @param makerAssetFillAmount Desired amount of makerAsset to buy. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketBuyOrders( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param makerAssetFillAmount Desired amount of makerAsset to buy. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketBuyOrdersNoThrow( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + public + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Synchronously cancels multiple orders in a single transaction. + /// @param orders Array of order specifications. + function batchCancelOrders(LibOrder.Order[] memory orders) + public; +} diff --git a/contracts/external/0x/Exchange/libs/LibConstants.sol b/contracts/external/0x/Exchange/libs/LibConstants.sol new file mode 100644 index 000000000..86f0704db --- /dev/null +++ b/contracts/external/0x/Exchange/libs/LibConstants.sol @@ -0,0 +1,27 @@ +/* + + 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 LibConstants { + + // Asset data for ZRX token. Used for fee transfers. + // @TODO: Hardcode constant when we deploy. Currently + // not constant to make testing easier. + bytes public ZRX_ASSET_DATA; +} diff --git a/contracts/external/0x/Exchange/libs/LibEIP712.sol b/contracts/external/0x/Exchange/libs/LibEIP712.sol new file mode 100644 index 000000000..aae266ad9 --- /dev/null +++ b/contracts/external/0x/Exchange/libs/LibEIP712.sol @@ -0,0 +1,42 @@ +/* + + 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 LibEIP712 { + // EIP191 header for EIP712 prefix + string constant EIP191_HEADER = "\x19\x01"; + + // EIP712 Domain Name value + string constant EIP712_DOMAIN_NAME = "0x Protocol"; + + // EIP712 Domain Version value + string constant EIP712_DOMAIN_VERSION = "2"; + + // Hash of the EIP712 Domain Separator Schema + bytes32 public constant EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked( + "EIP712Domain(", + "string name,", + "string version,", + "address verifyingContract", + ")" + )); + + // Hash of the EIP712 Domain Separator data + bytes32 public EIP712_DOMAIN_HASH; +} diff --git a/contracts/external/0x/Exchange/libs/LibExchangeErrors.sol b/contracts/external/0x/Exchange/libs/LibExchangeErrors.sol new file mode 100644 index 000000000..e37f41ada --- /dev/null +++ b/contracts/external/0x/Exchange/libs/LibExchangeErrors.sol @@ -0,0 +1,69 @@ +/* + + 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; + +/// @dev This contract documents the revert reasons used in the Exchange contract. +/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons. +contract LibExchangeErrors { + + /// Order validation errors /// + string constant ORDER_UNFILLABLE = "ORDER_UNFILLABLE"; // Order cannot be filled. + string constant INVALID_MAKER = "INVALID_MAKER"; // Invalid makerAddress. + string constant INVALID_TAKER = "INVALID_TAKER"; // Invalid takerAddress. + string constant INVALID_SENDER = "INVALID_SENDER"; // Invalid `msg.sender`. + string constant INVALID_ORDER_SIGNATURE = "INVALID_ORDER_SIGNATURE"; // Signature validation failed. + + /// fillOrder validation errors /// + string constant INVALID_TAKER_AMOUNT = "INVALID_TAKER_AMOUNT"; // takerAssetFillAmount cannot equal 0. + string constant ROUNDING_ERROR = "ROUNDING_ERROR"; // Rounding error greater than 0.1% of takerAssetFillAmount. + + /// Signature validation errors /// + string constant INVALID_SIGNATURE = "INVALID_SIGNATURE"; // Signature validation failed. + string constant SIGNATURE_ILLEGAL = "SIGNATURE_ILLEGAL"; // Signature type is illegal. + string constant SIGNATURE_UNSUPPORTED = "SIGNATURE_UNSUPPORTED"; // Signature type unsupported. + + /// cancelOrdersUptTo errors /// + string constant INVALID_NEW_ORDER_EPOCH = "INVALID_NEW_ORDER_EPOCH"; // Specified salt must be greater than or equal to existing orderEpoch. + + /// fillOrKillOrder errors /// + string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired takerAssetFillAmount could not be completely filled. + + /// matchOrders errors /// + string constant NEGATIVE_SPREAD_REQUIRED = "NEGATIVE_SPREAD_REQUIRED"; // Matched orders must have a negative spread. + + /// Transaction errors /// + string constant REENTRANCY_ILLEGAL = "REENTRANCY_ILLEGAL"; // Recursive reentrancy is not allowed. + string constant INVALID_TX_HASH = "INVALID_TX_HASH"; // Transaction has already been executed. + string constant INVALID_TX_SIGNATURE = "INVALID_TX_SIGNATURE"; // Signature validation failed. + string constant FAILED_EXECUTION = "FAILED_EXECUTION"; // Transaction execution failed. + + /// registerAssetProxy errors /// + string constant ASSET_PROXY_MISMATCH = "ASSET_PROXY_MISMATCH"; // oldAssetProxy proxy does not match currentAssetProxy. + string constant ASSET_PROXY_ID_MISMATCH = "ASSET_PROXY_ID_MISMATCH"; // newAssetProxyId does not match given assetProxyId. + + /// dispatchTransferFrom errors /// + string constant ASSET_PROXY_DOES_NOT_EXIST = "ASSET_PROXY_DOES_NOT_EXIST"; // No assetProxy registered at given id. + string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Asset transfer unsuccesful. + + /// Length validation errors /// + string constant LENGTH_GREATER_THAN_0_REQUIRED = "LENGTH_GREATER_THAN_0_REQUIRED"; // Byte array must have a length greater than 0. + string constant LENGTH_GREATER_THAN_3_REQUIRED = "LENGTH_GREATER_THAN_3_REQUIRED"; // Byte array must have a length greater than 3. + string constant LENGTH_0_REQUIRED = "LENGTH_0_REQUIRED"; // Byte array must have a length of 0. + string constant LENGTH_65_REQUIRED = "LENGTH_65_REQUIRED"; // Byte array must have a length of 65. +} diff --git a/contracts/external/0x/Exchange/libs/LibFillResults.sol b/contracts/external/0x/Exchange/libs/LibFillResults.sol new file mode 100644 index 000000000..a33ba388c --- /dev/null +++ b/contracts/external/0x/Exchange/libs/LibFillResults.sol @@ -0,0 +1,35 @@ +/* + + 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 LibFillResults +{ + struct FillResults { + uint256 makerAssetFilledAmount; // Total amount of makerAsset(s) filled. + uint256 takerAssetFilledAmount; // Total amount of takerAsset(s) filled. + uint256 makerFeePaid; // Total amount of ZRX paid by maker(s) to feeRecipient(s). + uint256 takerFeePaid; // Total amount of ZRX paid by taker to feeRecipients(s). + } + + struct MatchedFillResults { + FillResults left; // Amounts filled and fees paid of left order. + FillResults right; // Amounts filled and fees paid of right order. + uint256 leftMakerAssetSpreadAmount; // Spread between price of left and right order, denominated in the left order's makerAsset, paid to taker. + } +} diff --git a/contracts/external/0x/Exchange/libs/LibOrder.sol b/contracts/external/0x/Exchange/libs/LibOrder.sol new file mode 100644 index 000000000..8716441b5 --- /dev/null +++ b/contracts/external/0x/Exchange/libs/LibOrder.sol @@ -0,0 +1,77 @@ +/* + + 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 "./LibEIP712.sol"; + +contract LibOrder is + LibEIP712 +{ + + // Hash for the EIP712 Order Schema + bytes32 constant EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked( + "Order(", + "address makerAddress,", + "address takerAddress,", + "address feeRecipientAddress,", + "address senderAddress,", + "uint256 makerAssetAmount,", + "uint256 takerAssetAmount,", + "uint256 makerFee,", + "uint256 takerFee,", + "uint256 expirationTimeSeconds,", + "uint256 salt,", + "bytes makerAssetData,", + "bytes takerAssetData", + ")" + )); + + // A valid order remains fillable until it is expired, fully filled, or cancelled. + // An order's state is unaffected by external factors, like account balances. + enum OrderStatus { + INVALID, // Default value + INVALID_MAKER_ASSET_AMOUNT, // Order does not have a valid maker asset amount + INVALID_TAKER_ASSET_AMOUNT, // Order does not have a valid taker asset amount + FILLABLE, // Order is fillable + EXPIRED, // Order has already expired + FULLY_FILLED, // Order is fully filled + CANCELLED // Order has been cancelled + } + + struct Order { + address makerAddress; // Address that created the order. + address takerAddress; // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order. + address feeRecipientAddress; // Address that will recieve fees when order is filled. + address senderAddress; // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods. + uint256 makerAssetAmount; // Amount of makerAsset being offered by maker. Must be greater than 0. + uint256 takerAssetAmount; // Amount of takerAsset being bid on by maker. Must be greater than 0. + uint256 makerFee; // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted. + uint256 takerFee; // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted. + uint256 expirationTimeSeconds; // Timestamp in seconds at which order expires. + uint256 salt; // Arbitrary number to facilitate uniqueness of the order's hash. + bytes makerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy. + bytes takerAssetData; // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy. + } + + struct OrderInfo { + uint8 orderStatus; // Status that describes order's validity and fillability. + bytes32 orderHash; // EIP712 hash of the order (see LibOrder.getOrderHash). + uint256 orderTakerAssetFilledAmount; // Amount of order that has already been filled. + } +} diff --git a/contracts/external/LibBytes.sol b/contracts/external/LibBytes.sol index 1324a6637..af4fb38d5 100644 --- a/contracts/external/LibBytes.sol +++ b/contracts/external/LibBytes.sol @@ -31,6 +31,30 @@ library LibBytes { return memoryAddress; } + /// @dev Reads an unpadded bytes4 value from a position in a byte array. + /// @param b Byte array containing a bytes4 value. + /// @param index Index in byte array of bytes4 value. + /// @return bytes4 value from byte array. + function readBytes4( + bytes memory b, + uint256 index) + internal + pure + returns (bytes4 result) + { + require( + b.length >= index + 4, + "GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED" + ); + assembly { + result := mload(add(b, 32)) + // Solidity does not require us to clean the trailing bytes. + // We do it anyway + result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000) + } + return result; + } + /// @dev Reads a bytes32 value from a position in a byte array. /// @param b Byte array containing a bytes32 value. diff --git a/contracts/lib/CommonMath.sol b/contracts/lib/CommonMath.sol new file mode 100644 index 000000000..17cb48727 --- /dev/null +++ b/contracts/lib/CommonMath.sol @@ -0,0 +1,35 @@ +/* + Copyright 2018 Set Labs Inc. + + 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 { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; + +library CommonMath { + /** + * Calculates and returns the maximum value for a uint256 + * + * @return The maximum value for uint256 + */ + function maxUInt256() + internal + pure + returns (uint256) + { + return 2 ** 256 - 1; + } +} + \ No newline at end of file diff --git a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol b/contracts/test/lib/MockZeroExOrderDataHandlerLibrary.sol similarity index 70% rename from contracts/test/lib/ZeroExOrderDataHandlerLib.sol rename to contracts/test/lib/MockZeroExOrderDataHandlerLibrary.sol index 8b4f74d71..0588cd98e 100644 --- a/contracts/test/lib/ZeroExOrderDataHandlerLib.sol +++ b/contracts/test/lib/MockZeroExOrderDataHandlerLibrary.sol @@ -1,11 +1,15 @@ pragma solidity 0.4.24; pragma experimental "ABIEncoderV2"; -import { ZeroExOrderDataHandler } from "../../core/external/lib/ZeroExOrderDataHandler.sol"; +import { ZeroExOrderDataHandler } from "../../core/exchange-wrappers/lib/ZeroExOrderDataHandler.sol"; +import { LibBytes } from "../../external/LibBytes.sol"; +import { LibOrder } from "../../external/0x/Exchange/libs/LibOrder.sol"; // Mock class implementing internal OrderHandler methods contract MockZeroExOrderDataHandlerLibrary { + using LibBytes for bytes; + function parseOrderDataHeader(bytes _orderData) public pure @@ -33,9 +37,7 @@ contract MockZeroExOrderDataHandlerLibrary { pure returns (bytes) { - ZeroExOrderDataHandler.ZeroExHeader memory header = ZeroExOrderDataHandler.parseOrderHeader(_orderData); - uint256 signatureLength = header.signatureLength; - return ZeroExOrderDataHandler.sliceSignature(_orderData, signatureLength); + return ZeroExOrderDataHandler.sliceSignature(_orderData); } function parseZeroExOrderData(bytes _orderData) @@ -43,7 +45,7 @@ contract MockZeroExOrderDataHandlerLibrary { pure returns(address[4], uint256[6], bytes, bytes) { - ZeroExOrderDataHandler.Order memory order = ZeroExOrderDataHandler.parseZeroExOrderData(_orderData); + LibOrder.Order memory order = ZeroExOrderDataHandler.parseZeroExOrder(_orderData); return ( [ @@ -64,4 +66,12 @@ contract MockZeroExOrderDataHandlerLibrary { order.takerAssetData ); } + + function parseERC20TokenAddress(bytes _assetData) + public + returns (address) + { + address tokenAddress = ZeroExOrderDataHandler.parseERC20TokenAddress(_assetData); + return tokenAddress; + } } diff --git a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts index 0f7c441e3..13fe906d4 100644 --- a/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts +++ b/test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts @@ -20,6 +20,8 @@ import { createZeroExOrder, getZeroExOrderLengthFromBuffer, generateStandardZeroExOrderBytesArray, + generateERC20TokenAssetData, + getNumBytesFromHex, } from "../../../utils/zeroExExchangeWrapper"; // Testing Set up @@ -34,7 +36,14 @@ import { } from "../../../utils/constants"; contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { - const [ownerAccount, takerAddress, feeRecipientAddress, senderAddress] = accounts; + const [ + ownerAccount, + takerAddress, + feeRecipientAddress, + senderAddress, + makerTokenAddress, + takerTokenAddress + ] = accounts; let zeroExExchangeWrapper: MockZeroExOrderDataHandlerLibraryContract; // Signature @@ -49,8 +58,9 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { let takerFee = new BigNumber(4); let expirationTimeSeconds = new BigNumber(5); let salt = new BigNumber(6); - let makerAssetData = "ABC"; - let takerAssetData = "XYZ"; + + let makerAssetData = generateERC20TokenAssetData(makerTokenAddress); + let takerAssetData = generateERC20TokenAssetData(takerTokenAddress); let zeroExOrder: ZeroExOrder = createZeroExOrder( ownerAccount, @@ -99,8 +109,8 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { zeroExOrderLength = getZeroExOrderLengthFromBuffer(zeroExOrderBuffer); signatureLength = new BigNumber(signature.length); - makerAssetDataLength = new BigNumber(makerAssetData.length); - takerAssetDataLength = new BigNumber(takerAssetData.length); + makerAssetDataLength = getNumBytesFromHex(makerAssetData); + takerAssetDataLength = getNumBytesFromHex(takerAssetData); }); async function subject(): Promise { @@ -197,8 +207,26 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => { expect(takerFee).to.be.bignumber.equal(takerFeeResult); expect(expirationTimeSeconds).to.be.bignumber.equal(expirationResult); expect(salt).to.be.bignumber.equal(saltResult); - expect(makerAssetData).to.equal(web3.toAscii(makerAssetDataResult)); - expect(takerAssetData).to.equal(web3.toAscii(takerAssetDataResult)); + expect(makerAssetData).to.equal(makerAssetDataResult); + expect(takerAssetData).to.equal(takerAssetDataResult); }); }); + + describe("#parseERC20TokenAddress", async () => { + let subjectOrderData: Bytes32; + + beforeEach(async () => { + subjectOrderData = makerAssetData; + }); + + async function subject(): Promise { + return zeroExExchangeWrapper.parseERC20TokenAddress.callAsync(subjectOrderData); + } + + it("should correctly parse the maker token address", async () => { + const makerTokenAddressResult = await subject(); + expect(makerTokenAddressResult).to.equal(makerTokenAddress); + }); + + }); }); diff --git a/test/core/external/zeroExExchangeWrapper.spec.ts b/test/core/external/zeroExExchangeWrapper.spec.ts index 2e6cb1212..bb304875a 100644 --- a/test/core/external/zeroExExchangeWrapper.spec.ts +++ b/test/core/external/zeroExExchangeWrapper.spec.ts @@ -44,4 +44,9 @@ contract("ZeroExExchangeWrapper", (accounts) => { { from: ownerAccount }, ); }); + + describe("#exchange", async () => { + // Deploy a mock 0x exchange + // Deploy a mock 0x proxy + }); }); diff --git a/test/utils/zeroExExchangeWrapper.ts b/test/utils/zeroExExchangeWrapper.ts index a47afc285..1d842642c 100644 --- a/test/utils/zeroExExchangeWrapper.ts +++ b/test/utils/zeroExExchangeWrapper.ts @@ -11,6 +11,10 @@ function bufferAndLPad32(input: any): Buffer { return ethUtil.setLengthLeft(ethUtil.toBuffer(input), 32); } +export function getNumBytesFromHex(hexString: string): BigNumber { + return new BigNumber(hexString.length).minus(2).div(2) +} + export function createZeroExOrder( makerAddress: Address, takerAddress: Address, @@ -48,8 +52,8 @@ export function generateStandardZeroExOrderBytesArray( ) { const { makerAssetData, takerAssetData } = zeroExOrder; - const makerAssetDataLength = new BigNumber(makerAssetData.length); - const takerAssetDataLength = new BigNumber(takerAssetData.length); + const makerAssetDataLength = getNumBytesFromHex(makerAssetData); + const takerAssetDataLength = getNumBytesFromHex(makerAssetData); // Get signature length const signatureLength: UInt = new BigNumber(signature.length); @@ -97,6 +101,18 @@ export function bufferZeroExOrder( ]; } +export function generateERC20TokenAssetData( + tokenAddress: Address, +): string { + // The ERC20 asset data is always prefixed with 0xf47261b0 + // bytes4 ERC20_SELECTOR = bytes4(keccak256("ERC20Token(address)")); + const erc20AssetSelector = "0xf47261b0"; + + // Remove hex prefix and left pad to 32 bytes + const moddedTokenAddress = tokenAddress.slice(2).padStart(64, "0"); + return erc20AssetSelector.concat(moddedTokenAddress); +} + function bufferOrderHeader( signatureLength: UInt, orderLength: UInt,