diff options
author | Amir Bandeali <abandeali1@gmail.com> | 2018-08-15 06:39:10 +0800 |
---|---|---|
committer | Amir Bandeali <abandeali1@gmail.com> | 2018-08-22 02:47:28 +0800 |
commit | be67c25b0a3dcf2eaf91c39e4d8a8cdb1215bbe0 (patch) | |
tree | 7045a114ca79f5e572db3742015b1cb917efd598 /packages/contracts/src/2.0.0/extensions | |
parent | f53157414f144a7ea8c24126c9d75d3168228130 (diff) | |
download | dexon-0x-contracts-be67c25b0a3dcf2eaf91c39e4d8a8cdb1215bbe0.tar.gz dexon-0x-contracts-be67c25b0a3dcf2eaf91c39e4d8a8cdb1215bbe0.tar.zst dexon-0x-contracts-be67c25b0a3dcf2eaf91c39e4d8a8cdb1215bbe0.zip |
Add OrderValidator contract
Diffstat (limited to 'packages/contracts/src/2.0.0/extensions')
14 files changed, 1347 insertions, 0 deletions
diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol new file mode 100644 index 000000000..6b17bb29b --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/Forwarder.sol @@ -0,0 +1,51 @@ +/* + + 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 "./MixinWeth.sol"; +import "./MixinForwarderCore.sol"; +import "./libs/LibConstants.sol"; +import "./MixinAssets.sol"; +import "./MixinExchangeWrapper.sol"; + + +// solhint-disable no-empty-blocks +contract Forwarder is + LibConstants, + MixinWeth, + MixinAssets, + MixinExchangeWrapper, + MixinForwarderCore +{ + + constructor ( + address _exchange, + bytes memory _zrxAssetData, + bytes memory _wethAssetData + ) + public + LibConstants( + _exchange, + _zrxAssetData, + _wethAssetData + ) + MixinForwarderCore() + {} +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol new file mode 100644 index 000000000..d6a38aa6e --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinAssets.sol @@ -0,0 +1,144 @@ +/* + + 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/LibBytes/LibBytes.sol"; +import "../../utils/Ownable/Ownable.sol"; +import "../../tokens/ERC20Token/IERC20Token.sol"; +import "../../tokens/ERC721Token/IERC721Token.sol"; +import "./libs/LibConstants.sol"; +import "./mixins/MAssets.sol"; + + +contract MixinAssets is + Ownable, + LibConstants, + MAssets +{ + + using LibBytes for bytes; + + bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of ERC20 token to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + transferAssetToSender(assetData, amount); + } + + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + transferERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + transferERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + bool success = token.call(abi.encodeWithSelector( + ERC20_TRANSFER_SELECTOR, + msg.sender, + amount + )); + require( + success, + "TRANSFER_FAILED" + ); + + // Check return data. + // If there is no return data, we assume the token incorrectly + // does not return a bool. In this case we expect it to revert + // on failure, which was handled above. + // If the token does return data, we require that it is a single + // value that evaluates to true. + assembly { + if returndatasize { + success := 0 + if eq(returndatasize, 32) { + // First 64 bytes of memory are reserved scratch space + returndatacopy(0, 0, 32) + success := mload(0) + } + } + } + require( + success, + "TRANSFER_FAILED" + ); + } + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + require( + amount == 1, + "INVALID_AMOUNT" + ); + // Decode asset data. + address token = assetData.readAddress(16); + uint256 tokenId = assetData.readUint256(36); + + // Perform transfer. + IERC721Token(token).transferFrom( + address(this), + msg.sender, + tokenId + ); + } +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol new file mode 100644 index 000000000..218713d3c --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinExchangeWrapper.sol @@ -0,0 +1,263 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "./libs/LibConstants.sol"; +import "./mixins/MExchangeWrapper.sol"; +import "../../protocol/Exchange/libs/LibAbiEncoder.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; +import "../../protocol/Exchange/libs/LibFillResults.sol"; +import "../../protocol/Exchange/libs/LibMath.sol"; + + +contract MixinExchangeWrapper is + LibAbiEncoder, + LibFillResults, + LibMath, + LibConstants, + MExchangeWrapper +{ + + /// @dev Fills the input order. + /// Returns false if the transaction would otherwise revert. + /// @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 fillOrderNoThrow( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (FillResults memory fillResults) + { + // ABI encode calldata for `fillOrder` + bytes memory fillOrderCalldata = abiEncodeFillOrder( + order, + takerAssetFillAmount, + signature + ); + + address exchange = address(EXCHANGE); + + // Call `fillOrder` and handle any exceptions gracefully + assembly { + let success := call( + gas, // forward all gas, TODO: look into gas consumption of assert/throw + exchange, // call address of Exchange contract + 0, // transfer 0 wei + add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes) + mload(fillOrderCalldata), // length of input + fillOrderCalldata, // write output over input + 128 // output size is 128 bytes + ) + switch success + case 0 { + mstore(fillResults, 0) + mstore(add(fillResults, 32), 0) + mstore(add(fillResults, 64), 0) + mstore(add(fillResults, 96), 0) + } + case 1 { + mstore(fillResults, mload(fillOrderCalldata)) + mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32))) + mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64))) + mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96))) + } + } + return fillResults; + } + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param wethSellAmount Desired amount of WETH to sell. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellWeth( + LibOrder.Order[] memory orders, + uint256 wethSellAmount, + bytes[] memory signatures + ) + internal + returns (FillResults memory totalFillResults) + { + bytes memory makerAssetData = orders[0].makerAssetData; + bytes memory wethAssetData = WETH_ASSET_DATA; + + uint256 ordersLength = orders.length; + for (uint256 i = 0; i != ordersLength; i++) { + + // We assume that asset being bought by taker is the same for each order. + // We assume that asset being sold by taker is WETH for each order. + orders[i].makerAssetData = makerAssetData; + orders[i].takerAssetData = wethAssetData; + + // Calculate the remaining amount of WETH to sell + uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount); + + // Attempt to sell the remaining amount of WETH + FillResults memory singleFillResults = fillOrderNoThrow( + orders[i], + remainingTakerAssetFillAmount, + signatures[i] + ); + + // Update amounts filled and fees paid by maker and taker + addFillResults(totalFillResults, singleFillResults); + + // Stop execution if the entire amount of takerAsset has been sold + if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) { + break; + } + } + return 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. + /// The asset being sold by taker must always be WETH. + /// @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 marketBuyExactAmountWithWeth( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + internal + returns (FillResults memory totalFillResults) + { + bytes memory makerAssetData = orders[0].makerAssetData; + bytes memory wethAssetData = WETH_ASSET_DATA; + + uint256 ordersLength = orders.length; + for (uint256 i = 0; i != ordersLength; i++) { + + // We assume that asset being bought by taker is the same for each order. + // We assume that asset being sold by taker is WETH for each order. + orders[i].makerAssetData = makerAssetData; + orders[i].takerAssetData = wethAssetData; + + // Calculate the remaining amount of makerAsset to buy + uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount); + + // Convert the remaining amount of makerAsset to buy into remaining amount + // of takerAsset to sell, assuming entire amount can be sold in the current order + uint256 remainingTakerAssetFillAmount = getPartialAmount( + orders[i].takerAssetAmount, + orders[i].makerAssetAmount, + remainingMakerAssetFillAmount + ); + + // Attempt to sell the remaining amount of takerAsset + FillResults memory singleFillResults = fillOrderNoThrow( + orders[i], + remainingTakerAssetFillAmount, + signatures[i] + ); + + // Update amounts filled and fees paid by maker and taker + addFillResults(totalFillResults, singleFillResults); + + // Stop execution if the entire amount of makerAsset has been bought + uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount; + if (makerAssetFilledAmount >= makerAssetFillAmount) { + break; + } + } + + require( + makerAssetFilledAmount >= makerAssetFillAmount, + "COMPLETE_FILL_FAILED" + ); + return totalFillResults; + } + + /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee + /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues). + /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX + /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases. + /// The asset being sold by taker must always be WETH. + /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. + /// @param zrxBuyAmount Desired amount of ZRX to buy. + /// @param signatures Proofs that orders have been created by makers. + /// @return totalFillResults Amounts filled and fees paid by maker and taker. + function marketBuyExactZrxWithWeth( + LibOrder.Order[] memory orders, + uint256 zrxBuyAmount, + bytes[] memory signatures + ) + internal + returns (FillResults memory totalFillResults) + { + // Do nothing if zrxBuyAmount == 0 + if (zrxBuyAmount == 0) { + return totalFillResults; + } + + bytes memory zrxAssetData = ZRX_ASSET_DATA; + bytes memory wethAssetData = WETH_ASSET_DATA; + uint256 zrxPurchased = 0; + + uint256 ordersLength = orders.length; + for (uint256 i = 0; i != ordersLength; i++) { + + // All of these are ZRX/WETH, so we can drop the respective assetData from calldata. + orders[i].makerAssetData = zrxAssetData; + orders[i].takerAssetData = wethAssetData; + + // Calculate the remaining amount of ZRX to buy. + uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased); + + // Convert the remaining amount of ZRX to buy into remaining amount + // of WETH to sell, assuming entire amount can be sold in the current order. + uint256 remainingWethSellAmount = getPartialAmount( + orders[i].takerAssetAmount, + safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees + remainingZrxBuyAmount + ); + + // Attempt to sell the remaining amount of WETH. + FillResults memory singleFillResult = fillOrderNoThrow( + orders[i], + safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors + signatures[i] + ); + + // Update amounts filled and fees paid by maker and taker. + addFillResults(totalFillResults, singleFillResult); + zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid); + + // Stop execution if the entire amount of ZRX has been bought. + if (zrxPurchased >= zrxBuyAmount) { + break; + } + } + + require( + zrxPurchased >= zrxBuyAmount, + "COMPLETE_FILL_FAILED" + ); + return totalFillResults; + } +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol new file mode 100644 index 000000000..42cec4d36 --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinForwarderCore.sol @@ -0,0 +1,213 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; + +import "./libs/LibConstants.sol"; +import "./mixins/MWeth.sol"; +import "./mixins/MAssets.sol"; +import "./mixins/MExchangeWrapper.sol"; +import "./interfaces/IForwarderCore.sol"; +import "../../utils/LibBytes/LibBytes.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; +import "../../protocol/Exchange/libs/LibFillResults.sol"; +import "../../protocol/Exchange/libs/LibMath.sol"; + + +contract MixinForwarderCore is + LibFillResults, + LibMath, + LibConstants, + MWeth, + MAssets, + MExchangeWrapper, + IForwarderCore +{ + + using LibBytes for bytes; + + /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf. + constructor () + public + { + address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID); + if (proxyAddress != address(0)) { + ETHER_TOKEN.approve(proxyAddress, MAX_UINT); + ZRX_TOKEN.approve(proxyAddress, MAX_UINT); + } + } + + /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value. + /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. + /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH). + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param signatures Proofs that orders have been created by makers. + /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. + /// @param feeSignatures Proofs that feeOrders have been created by makers. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + /// @return Amounts filled and fees paid by maker and taker for both sets of orders. + function marketSellOrdersWithEth( + LibOrder.Order[] memory orders, + bytes[] memory signatures, + LibOrder.Order[] memory feeOrders, + bytes[] memory feeSignatures, + uint256 feePercentage, + address feeRecipient + ) + public + payable + returns ( + FillResults memory orderFillResults, + FillResults memory feeOrderFillResults + ) + { + // Convert ETH to WETH. + convertEthToWeth(); + + uint256 wethSellAmount; + uint256 zrxBuyAmount; + uint256 makerAssetAmountPurchased; + if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) { + // Calculate amount of WETH that won't be spent on ETH fees. + wethSellAmount = getPartialAmount( + PERCENTAGE_DENOMINATOR, + safeAdd(PERCENTAGE_DENOMINATOR, feePercentage), + msg.value + ); + // Market sell available WETH. + // ZRX fees are paid with this contract's balance. + orderFillResults = marketSellWeth( + orders, + wethSellAmount, + signatures + ); + // The fee amount must be deducted from the amount transfered back to sender. + makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid); + } else { + // 5% of WETH is reserved for filling feeOrders and paying feeRecipient. + wethSellAmount = getPartialAmount( + MAX_WETH_FILL_PERCENTAGE, + PERCENTAGE_DENOMINATOR, + msg.value + ); + // Market sell 95% of WETH. + // ZRX fees are payed with this contract's balance. + orderFillResults = marketSellWeth( + orders, + wethSellAmount, + signatures + ); + // Buy back all ZRX spent on fees. + zrxBuyAmount = orderFillResults.takerFeePaid; + feeOrderFillResults = marketBuyExactZrxWithWeth( + feeOrders, + zrxBuyAmount, + feeSignatures + ); + makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount; + } + + // Transfer feePercentage of total ETH spent on primary orders to feeRecipient. + // Refund remaining ETH to msg.sender. + transferEthFeeAndRefund( + orderFillResults.takerAssetFilledAmount, + feeOrderFillResults.takerAssetFilledAmount, + feePercentage, + feeRecipient + ); + + // Transfer purchased assets to msg.sender. + transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased); + } + + /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction. + /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param makerAssetFillAmount Desired amount of makerAsset to purchase. + /// @param signatures Proofs that orders have been created by makers. + /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. + /// @param feeSignatures Proofs that feeOrders have been created by makers. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + /// @return Amounts filled and fees paid by maker and taker for both sets of orders. + function marketBuyOrdersWithEth( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures, + LibOrder.Order[] memory feeOrders, + bytes[] memory feeSignatures, + uint256 feePercentage, + address feeRecipient + ) + public + payable + returns ( + FillResults memory orderFillResults, + FillResults memory feeOrderFillResults + ) + { + // Convert ETH to WETH. + convertEthToWeth(); + + uint256 zrxBuyAmount; + uint256 makerAssetAmountPurchased; + if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) { + // If the makerAsset is ZRX, it is not necessary to pay fees out of this + // contracts's ZRX balance because fees are factored into the price of the order. + orderFillResults = marketBuyExactZrxWithWeth( + orders, + makerAssetFillAmount, + signatures + ); + // The fee amount must be deducted from the amount transfered back to sender. + makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid); + } else { + // Attemp to purchase desired amount of makerAsset. + // ZRX fees are payed with this contract's balance. + orderFillResults = marketBuyExactAmountWithWeth( + orders, + makerAssetFillAmount, + signatures + ); + // Buy back all ZRX spent on fees. + zrxBuyAmount = orderFillResults.takerFeePaid; + feeOrderFillResults = marketBuyExactZrxWithWeth( + feeOrders, + zrxBuyAmount, + feeSignatures + ); + makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount; + } + + // Transfer feePercentage of total ETH spent on primary orders to feeRecipient. + // Refund remaining ETH to msg.sender. + transferEthFeeAndRefund( + orderFillResults.takerAssetFilledAmount, + feeOrderFillResults.takerAssetFilledAmount, + feePercentage, + feeRecipient + ); + + // Transfer purchased assets to msg.sender. + transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased); + } +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol new file mode 100644 index 000000000..93e85e599 --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/MixinWeth.sol @@ -0,0 +1,114 @@ +/* + + 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 "../../protocol/Exchange/libs/LibMath.sol"; +import "./libs/LibConstants.sol"; +import "./mixins/MWeth.sol"; + + +contract MixinWeth is + LibMath, + LibConstants, + MWeth +{ + + /// @dev Default payabale function, this allows us to withdraw WETH + function () + public + payable + { + require( + msg.sender == address(ETHER_TOKEN), + "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY" + ); + } + + /// @dev Converts message call's ETH value into WETH. + function convertEthToWeth() + internal + { + require( + msg.value > 0, + "INVALID_MSG_VALUE" + ); + ETHER_TOKEN.deposit.value(msg.value)(); + } + + /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient. + /// Refunds any excess ETH to msg.sender. + /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders. + /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + function transferEthFeeAndRefund( + uint256 wethSoldExcludingFeeOrders, + uint256 wethSoldForZrx, + uint256 feePercentage, + address feeRecipient + ) + internal + { + // Ensure feePercentage is less than 5%. + require( + feePercentage <= MAX_FEE_PERCENTAGE, + "FEE_PERCENTAGE_TOO_LARGE" + ); + + // Ensure that no extra WETH owned by this contract has been sold. + uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx); + require( + wethSold <= msg.value, + "OVERSOLD_WETH" + ); + + // Calculate amount of WETH that hasn't been sold. + uint256 wethRemaining = safeSub(msg.value, wethSold); + + // Calculate ETH fee to pay to feeRecipient. + uint256 ethFee = getPartialAmount( + feePercentage, + PERCENTAGE_DENOMINATOR, + wethSoldExcludingFeeOrders + ); + + // Ensure fee is less than amount of WETH remaining. + require( + ethFee <= wethRemaining, + "INSUFFICIENT_ETH_REMAINING" + ); + + // Do nothing if no WETH remaining + if (wethRemaining > 0) { + // Convert remaining WETH to ETH + ETHER_TOKEN.withdraw(wethRemaining); + + // Pay ETH to feeRecipient + if (ethFee > 0) { + feeRecipient.transfer(ethFee); + } + + // Refund remaining ETH to msg.sender. + uint256 ethRefund = safeSub(wethRemaining, ethFee); + if (ethRefund > 0) { + msg.sender.transfer(ethRefund); + } + } + } +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IAssets.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IAssets.sol new file mode 100644 index 000000000..1e034c003 --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IAssets.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 IAssets { + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of ERC20 token to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external; +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarder.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarder.sol new file mode 100644 index 000000000..f5a26e2ba --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarder.sol @@ -0,0 +1,30 @@ +/* + + 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 "./IForwarderCore.sol"; +import "./IAssets.sol"; + + +// solhint-disable no-empty-blocks +contract IForwarder is + IForwarderCore, + IAssets +{} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarderCore.sol new file mode 100644 index 000000000..74c7da01d --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/interfaces/IForwarderCore.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.24; +pragma experimental ABIEncoderV2; + +import "../../../protocol/Exchange/libs/LibOrder.sol"; +import "../../../protocol/Exchange/libs/LibFillResults.sol"; + + +contract IForwarderCore { + + /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value. + /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. + /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH). + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param signatures Proofs that orders have been created by makers. + /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. + /// @param feeSignatures Proofs that feeOrders have been created by makers. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + /// @return Amounts filled and fees paid by maker and taker for both sets of orders. + function marketSellOrdersWithEth( + LibOrder.Order[] memory orders, + bytes[] memory signatures, + LibOrder.Order[] memory feeOrders, + bytes[] memory feeSignatures, + uint256 feePercentage, + address feeRecipient + ) + public + payable + returns ( + LibFillResults.FillResults memory orderFillResults, + LibFillResults.FillResults memory feeOrderFillResults + ); + + /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction. + /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param makerAssetFillAmount Desired amount of makerAsset to purchase. + /// @param signatures Proofs that orders have been created by makers. + /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. + /// @param feeSignatures Proofs that feeOrders have been created by makers. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + /// @return Amounts filled and fees paid by maker and taker for both sets of orders. + function marketBuyOrdersWithEth( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures, + LibOrder.Order[] memory feeOrders, + bytes[] memory feeSignatures, + uint256 feePercentage, + address feeRecipient + ) + public + payable + returns ( + LibFillResults.FillResults memory orderFillResults, + LibFillResults.FillResults memory feeOrderFillResults + ); +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibConstants.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibConstants.sol new file mode 100644 index 000000000..704e42ce3 --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibConstants.sol @@ -0,0 +1,62 @@ +/* + + 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/LibBytes/LibBytes.sol"; +import "../../../protocol/Exchange/interfaces/IExchange.sol"; +import "../../../tokens/EtherToken/IEtherToken.sol"; +import "../../../tokens/ERC20Token/IERC20Token.sol"; + + +contract LibConstants { + + using LibBytes for bytes; + + bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); + bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); + uint256 constant internal MAX_UINT = 2**256 - 1; + uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18; + uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5% + uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95% + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + IEtherToken internal ETHER_TOKEN; + IERC20Token internal ZRX_TOKEN; + bytes internal ZRX_ASSET_DATA; + bytes internal WETH_ASSET_DATA; + // solhint-enable var-name-mixedcase + + constructor ( + address _exchange, + bytes memory _zrxAssetData, + bytes memory _wethAssetData + ) + public + { + EXCHANGE = IExchange(_exchange); + ZRX_ASSET_DATA = _zrxAssetData; + WETH_ASSET_DATA = _wethAssetData; + + address etherToken = _wethAssetData.readAddress(16); + address zrxToken = _zrxAssetData.readAddress(16); + ETHER_TOKEN = IEtherToken(etherToken); + ZRX_TOKEN = IERC20Token(zrxToken); + } +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibForwarderErrors.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibForwarderErrors.sol new file mode 100644 index 000000000..fb3ade1db --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/libs/LibForwarderErrors.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. + +*/ + +// solhint-disable +pragma solidity 0.4.24; + + +/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons. +contract LibForwarderErrors { + string constant FEE_PERCENTAGE_TOO_LARGE = "FEE_PROPORTION_TOO_LARGE"; // Provided fee percentage greater than 5%. + string constant INSUFFICIENT_ETH_REMAINING = "INSUFFICIENT_ETH_REMAINING"; // Not enough ETH remaining to pay feeRecipient. + string constant OVERSOLD_WETH = "OVERSOLD_WETH"; // More WETH sold than provided with current message call. + string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired purchase amount not completely filled (required for ZRX fees only). + string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Asset transfer failed. + string constant UNSUPPORTED_ASSET_PROXY = "UNSUPPORTED_ASSET_PROXY"; // Proxy in assetData not supported. + string constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY = "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"; // Fallback function may only be used for WETH withdrawals. + string constant INVALID_MSG_VALUE = "INVALID_MSG_VALUE"; // msg.value must be greater than 0. + string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Amount must equal 1. +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol new file mode 100644 index 000000000..83636432a --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MAssets.sol @@ -0,0 +1,54 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../interfaces/IAssets.sol"; + + +contract MAssets is + IAssets +{ + + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol new file mode 100644 index 000000000..13c26b03a --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MExchangeWrapper.sol @@ -0,0 +1,87 @@ +/* + + 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 "../../../protocol/Exchange/libs/LibOrder.sol"; +import "../../../protocol/Exchange/libs/LibFillResults.sol"; + + +contract MExchangeWrapper { + + /// @dev Fills the input order. + /// Returns false if the transaction would otherwise revert. + /// @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 fillOrderNoThrow( + LibOrder.Order memory order, + uint256 takerAssetFillAmount, + bytes memory signature + ) + internal + returns (LibFillResults.FillResults memory fillResults); + + /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker. + /// Returns false if the transaction would otherwise revert. + /// @param orders Array of order specifications. + /// @param wethSellAmount Desired amount of WETH to sell. + /// @param signatures Proofs that orders have been signed by makers. + /// @return Amounts filled and fees paid by makers and taker. + function marketSellWeth( + LibOrder.Order[] memory orders, + uint256 wethSellAmount, + bytes[] memory signatures + ) + internal + 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. + /// The asset being sold by taker must always be WETH. + /// @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 marketBuyExactAmountWithWeth( + LibOrder.Order[] memory orders, + uint256 makerAssetFillAmount, + bytes[] memory signatures + ) + internal + returns (LibFillResults.FillResults memory totalFillResults); + + /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee + /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues). + /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX + /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases. + /// The asset being sold by taker must always be WETH. + /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. + /// @param zrxBuyAmount Desired amount of ZRX to buy. + /// @param signatures Proofs that orders have been created by makers. + /// @return totalFillResults Amounts filled and fees paid by maker and taker. + function marketBuyExactZrxWithWeth( + LibOrder.Order[] memory orders, + uint256 zrxBuyAmount, + bytes[] memory signatures + ) + internal + returns (LibFillResults.FillResults memory totalFillResults); +} diff --git a/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MWeth.sol b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MWeth.sol new file mode 100644 index 000000000..88e77be4e --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/Forwarder/mixins/MWeth.sol @@ -0,0 +1,41 @@ +/* + + 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 MWeth { + + /// @dev Converts message call's ETH value into WETH. + function convertEthToWeth() + internal; + + /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient. + /// Refunds any excess ETH to msg.sender. + /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders. + /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees. + /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + /// @param feeRecipient Address that will receive ETH when orders are filled. + function transferEthFeeAndRefund( + uint256 wethSoldExcludingFeeOrders, + uint256 wethSoldForZrx, + uint256 feePercentage, + address feeRecipient + ) + internal; +} diff --git a/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol new file mode 100644 index 000000000..dcbbd7fa5 --- /dev/null +++ b/packages/contracts/src/2.0.0/extensions/OrderValidator/OrderValidator.sol @@ -0,0 +1,140 @@ +/* + + 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 "../../protocol/Exchange/interfaces/IExchange.sol"; +import "../../protocol/Exchange/libs/LibOrder.sol"; +import "../../tokens/ERC20Token/IERC20Token.sol"; +import "../../tokens/ERC721Token/IERC721Token.sol"; +import "../../utils/LibBytes/LibBytes.sol"; + + +contract OrderValidator { + + bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); + bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); + + using LibBytes for bytes; + + struct TraderInfo { + uint256 makerBalance; // Maker's balance of makerAsset + uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy + uint256 takerBalance; // Taker's balance of takerAsset + uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy + } + + // Exchange contract. + // solhint-disable-next-line var-name-mixedcase + IExchange internal EXCHANGE; + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + } + + /// @dev Fetches information for order and maker/taker of order. + /// @param order The order structure. + /// @param takerAddress Address that will be filling the order. + /// @return OrderInfo and TraderInfo instances for given order. + function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress) + public + view + returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo) + { + orderInfo = EXCHANGE.getOrderInfo(order); + traderInfo = getTraderInfo(order, takerAddress); + return (orderInfo, traderInfo); + } + + /// @dev Fetches information for all passed in orders and the makers/takers of each order. + /// @param orders Array of order specifications. + /// @param takerAddresses Array of taker addresses corresponding to each order. + /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order. + function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses) + public + view + returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo) + { + ordersInfo = EXCHANGE.getOrdersInfo(orders); + tradersInfo = getTradersInfo(orders, takerAddresses); + return (ordersInfo, tradersInfo); + } + + /// @dev Fetches balance and allowances for maker and taker of order. + /// @param order The order structure. + /// @param takerAddress Address that will be filling the order. + /// @return Balances and allowances of maker and taker of order. + function getTraderInfo(LibOrder.Order memory order, address takerAddress) + public + view + returns (TraderInfo memory traderInfo) + { + (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData); + (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData); + return traderInfo; + } + + /// @dev Fetches balances and allowances of maker and taker for each provided order. + /// @param orders Array of order specifications. + /// @param takerAddresses Array of taker addresses corresponding to each order. + /// @return Array of balances and allowances for maker and taker of each order. + function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses) + public + view + returns (TraderInfo[] memory) + { + uint256 ordersLength = orders.length; + TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength); + for (uint256 i = 0; i != ordersLength; i++) { + tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]); + } + return tradersInfo; + } + + /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721. + /// @param target Address to fetch balances and allowances of. + /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset. + /// @return Balance of asset and allowance set to given proxy of asset. + /// For ERC721 tokens, these values will always be 1 or 0. + function getBalanceAndAllowance(address target, bytes memory assetData) + public + view + returns (uint256 balance, uint256 allowance) + { + bytes4 assetProxyId = assetData.readBytes4(0); + address token = assetData.readAddress(16); + address assetProxy = EXCHANGE.getAssetProxy(assetProxyId); + + if (assetProxyId == ERC20_DATA_ID) { + balance = IERC20Token(token).balanceOf(target); + allowance = IERC20Token(token).allowance(target, assetProxy); + } else if (assetProxyId == ERC721_DATA_ID) { + uint256 tokenId = assetData.readUint256(36); + address owner = IERC721Token(token).ownerOf(tokenId); + balance = target == owner ? 1 : 0; + bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy) || IERC721Token(token).getApproved(tokenId) == assetProxy; + allowance = isApproved ? 1 : 0; + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + return (balance, allowance); + } +} |