From 2fcc36bbadcb8238ee292afc58c2cd460d1b69da Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 26 Jun 2018 19:12:43 -0700 Subject: Update file structure --- .../2.0.0/protocol/Exchange/libs/LibConstants.sol | 34 ++++++ .../src/2.0.0/protocol/Exchange/libs/LibEIP712.sol | 64 ++++++++++ .../protocol/Exchange/libs/LibExchangeErrors.sol | 68 +++++++++++ .../protocol/Exchange/libs/LibFillResults.sol | 53 ++++++++ .../src/2.0.0/protocol/Exchange/libs/LibMath.sol | 72 +++++++++++ .../src/2.0.0/protocol/Exchange/libs/LibOrder.sol | 135 +++++++++++++++++++++ 6 files changed, 426 insertions(+) create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol create mode 100644 packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol (limited to 'packages/contracts/src/2.0.0/protocol/Exchange/libs') diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol new file mode 100644 index 000000000..488ca956c --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.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 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; + + // @TODO: Remove when we deploy. + constructor (bytes memory zrxAssetData) + public + { + ZRX_ASSET_DATA = zrxAssetData; + } +} diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol new file mode 100644 index 000000000..b983347a4 --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibEIP712.sol @@ -0,0 +1,64 @@ +/* + + 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; + + constructor () + public + { + EIP712_DOMAIN_HASH = keccak256(abi.encode( + EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, + keccak256(bytes(EIP712_DOMAIN_NAME)), + keccak256(bytes(EIP712_DOMAIN_VERSION)), + address(this) + )); + } + + /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain. + /// @param hashStruct The EIP712 hash struct. + /// @return EIP712 hash applied to this EIP712 Domain. + function hashEIP712Message(bytes32 hashStruct) + internal + view + returns (bytes32) + { + return keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, hashStruct)); + } +} diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol new file mode 100644 index 000000000..01aa78a1d --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibExchangeErrors.sol @@ -0,0 +1,68 @@ +/* + + 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_ALREADY_EXISTS = "ASSET_PROXY_ALREADY_EXISTS"; // AssetProxy with same id already exists. + + /// 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/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol new file mode 100644 index 000000000..63f1b8c87 --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibFillResults.sol @@ -0,0 +1,53 @@ +/* + + 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 "../../../utils/SafeMath/SafeMath.sol"; + +contract LibFillResults is + SafeMath +{ + + 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. + } + + /// @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/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol new file mode 100644 index 000000000..bfe2fd33f --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol @@ -0,0 +1,72 @@ +/* + + 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 "../../../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/2.0.0/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol new file mode 100644 index 000000000..954f94f76 --- /dev/null +++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol @@ -0,0 +1,135 @@ +/* + + 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. + } + + /// @dev Calculates Keccak-256 hash of the order. + /// @param order The order structure. + /// @return Keccak-256 EIP712 hash of the order. + function getOrderHash(Order memory order) + internal + view + returns (bytes32 orderHash) + { + orderHash = hashEIP712Message(hashOrder(order)); + return orderHash; + } + + /// @dev Calculates EIP712 hash of the order. + /// @param order The order structure. + /// @return EIP712 hash of the order. + function hashOrder(Order memory order) + internal + pure + returns (bytes32 result) + { + bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH; + bytes32 makerAssetDataHash = keccak256(order.makerAssetData); + bytes32 takerAssetDataHash = keccak256(order.takerAssetData); + // Assembly for more efficiently computing: + // keccak256(abi.encode( + // order.makerAddress, + // order.takerAddress, + // order.feeRecipientAddress, + // order.senderAddress, + // order.makerAssetAmount, + // order.takerAssetAmount, + // order.makerFee, + // order.takerFee, + // order.expirationTimeSeconds, + // order.salt, + // keccak256(order.makerAssetData), + // keccak256(order.takerAssetData) + // )); + assembly { + // Backup + let temp1 := mload(sub(order, 32)) + let temp2 := mload(add(order, 320)) + let temp3 := mload(add(order, 352)) + + // Hash in place + mstore(sub(order, 32), schemaHash) + mstore(add(order, 320), makerAssetDataHash) + mstore(add(order, 352), takerAssetDataHash) + result := keccak256(sub(order, 32), 416) + + // Restore + mstore(sub(order, 32), temp1) + mstore(add(order, 320), temp2) + mstore(add(order, 352), temp3) + } + return result; + } +} -- cgit v1.2.3