diff options
author | Amir Bandeali <abandeali1@gmail.com> | 2018-07-10 12:09:45 +0800 |
---|---|---|
committer | Amir Bandeali <abandeali1@gmail.com> | 2018-07-23 23:00:23 +0800 |
commit | 814518dd8094d03908d77e39faa21b8758f1552b (patch) | |
tree | 7da9bd14251b2f1e3c23a8bbbd844c11c8a2b1d0 /packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol | |
parent | 554b18a466d5cc65271daa61aba96138ce7408f0 (diff) | |
download | dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar.gz dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar.bz2 dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar.lz dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar.xz dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.tar.zst dexon-0x-contracts-814518dd8094d03908d77e39faa21b8758f1552b.zip |
Refactor forwarding contract architecture, remove batch functions
Diffstat (limited to 'packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol')
-rw-r--r-- | packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol | 519 |
1 files changed, 197 insertions, 322 deletions
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol index cb0ed5422..1a0687ab5 100644 --- a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol +++ b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol @@ -19,30 +19,25 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../utils/LibBytes/LibBytes.sol"; -import "./mixins/MFees.sol"; -import "./mixins/MMarketBuyZrx.sol"; -import "./mixins/MExpectedResults.sol"; -import "./mixins/MTransfer.sol"; +import "./mixins/MWeth.sol"; +import "./mixins/MAssets.sol"; import "./mixins/MConstants.sol"; import "./mixins/MForwarderCore.sol"; import "../protocol/Exchange/libs/LibOrder.sol"; import "../protocol/Exchange/libs/LibFillResults.sol"; +import "../protocol/Exchange/libs/LibMath.sol"; contract MixinForwarderCore is LibFillResults, + LibMath, MConstants, - MExpectedResults, - MFees, - MMarketBuyZrx, - MTransfer, + MWeth, + MAssets, MForwarderCore { - 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; + /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf. constructor () public { @@ -53,377 +48,257 @@ contract MixinForwarderCore is } } - /// @dev Market sells ETH for ERC20 tokens, performing fee abstraction if required. This does not support ERC721 tokens. This function is payable - /// and will convert all incoming ETH into WETH and perform the trade on behalf of the caller. - /// This function allows for a deduction of a proportion of incoming ETH sent to the feeRecipient. - /// The caller is sent all tokens from the operation. - /// If the purchased token amount does not meet an acceptable threshold then this function reverts. - /// @param orders An array of Order struct containing order specifications. - /// @param signatures An array of Proof that order has been created by maker. - /// @param feeOrders An array of Order struct containing order specifications for fees. - /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders. - /// @param feeProportion A proportion deducted off the incoming ETH and sent to feeRecipient. The maximum value for this - /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59. - /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH. - /// @return FillResults amounts filled and fees paid by maker and taker. - function marketSellEthForERC20( + /// @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, - uint16 feeProportion, + uint256 feePercentage, address feeRecipient ) public payable - returns (FillResults memory totalFillResults) + returns ( + FillResults memory orderFillResults, + FillResults memory feeOrderFillResults + ) { - uint256 takerEthAmount = msg.value; - require( - takerEthAmount > 0, - "VALUE_GREATER_THAN_ZERO" - ); - // Deduct the fee from the total amount of ETH sent in - uint256 ethFeeAmount = payEthFee( - takerEthAmount, - feeProportion, - feeRecipient + // Convert ETH to WETH. + // 5% of WETH is reserved for filling feeOrders and paying feeRecipient. + uint256 wethAvailable = convertEthToWeth(); + + // Attempt to sell 95% of WETH. + // ZRX fees are payed with this contract's balance. + marketSellEth( + orders, + wethAvailable, + signatures ); - uint256 wethSellAmount = safeSub(takerEthAmount, ethFeeAmount); - // Deposit the remaining to be used for trading - ETHER_TOKEN.deposit.value(wethSellAmount)(); - // Populate the known assetData, as it is always WETH the caller can provide null bytes to save gas - // marketSellOrders fills the remaining - address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16); - orders[0].takerAssetData = WETH_ASSET_DATA; - if (makerTokenAddress == address(ZRX_TOKEN)) { - // If this is ZRX then we market sell from the orders, rather than a 2 step of buying ZRX fees from feeOrders - // then buying ZRX from orders - totalFillResults = marketSellEthForZRXInternal( - orders, - signatures, - wethSellAmount - ); - } else { - totalFillResults = marketSellEthForERC20Internal( - orders, - signatures, - feeOrders, - feeSignatures, - wethSellAmount - ); - } - // Prevent accidental WETH owned by this contract and it being spent - require( - takerEthAmount >= totalFillResults.takerAssetFilledAmount, - "INVALID_MSG_VALUE" + // Buy back all ZRX spent on fees. + feeOrderFillResults = marketBuyZrx( + feeOrders, + orderFillResults.takerFeePaid, + feeSignatures ); - // Ensure no WETH is left in this contract + + // Ensure that no extra WETH owned by this contract has been sold. + uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount); require( - wethSellAmount == totalFillResults.takerAssetFilledAmount, - "UNACCEPTABLE_THRESHOLD" + totalWethSold <= msg.value, + "OVERSOLD_WETH" ); - // Transfer all tokens to msg.sender - transferERC20Token( - makerTokenAddress, - msg.sender, - totalFillResults.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 ); - return totalFillResults; + + // Transfer purchased assets to msg.sender. + transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount); } - /// @dev Buys the exact amount of assets (ERC20 and ERC721), performing fee abstraction if required. - /// All order assets must be of the same type. Deducts a proportional fee to fee recipient. - /// This function is payable and will convert all incoming ETH into WETH and perform the trade on behalf of the caller. - /// The caller is sent all assets from the fill of orders. This function will revert unless the requested amount of assets are purchased. - /// Any excess ETH sent will be returned to the caller - /// @param orders An array of Order struct containing order specifications. - /// @param signatures An array of Proof that order has been created by maker. - /// @param feeOrders An array of Order struct containing order specifications for fees. - /// @param makerTokenFillAmount The amount of maker asset to buy. - /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders. - /// @param feeProportion A proportion deducted off the ETH spent and sent to feeRecipient. The maximum value for this - /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59. - /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH. - /// @return FillResults amounts filled and fees paid by maker and taker. - function marketBuyTokensWithEth( + /// @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 makerTokenFillAmount, - uint16 feeProportion, + uint256 feePercentage, address feeRecipient ) public payable - returns (FillResults memory totalFillResults) + returns ( + FillResults memory orderFillResults, + FillResults memory feeOrderFillResults + ) { - uint256 takerEthAmount = msg.value; - require( - takerEthAmount > 0, - "VALUE_GREATER_THAN_ZERO" - ); - require( - makerTokenFillAmount > 0, - "VALUE_GREATER_THAN_ZERO" + // Convert ETH to WETH. + convertEthToWeth(); + + // Attemp to purchase desired amount of makerAsset. + // ZRX fees are payed with this contract's balance. + orderFillResults = marketBuyAsset( + orders, + makerAssetFillAmount, + signatures ); - bytes4 assetDataId = LibBytes.readBytes4(orders[0].makerAssetData, 0); - require( - assetDataId == ERC20_DATA_ID || assetDataId == ERC721_DATA_ID, - "UNSUPPORTED_TOKEN_PROXY" + + // Buy back all ZRX spent on fees. + feeOrderFillResults = marketBuyZrx( + feeOrders, + orderFillResults.takerFeePaid, + feeSignatures ); - ETHER_TOKEN.deposit.value(takerEthAmount)(); - if (assetDataId == ERC20_DATA_ID) { - totalFillResults = marketBuyERC20TokensInternal( - orders, - signatures, - feeOrders, - feeSignatures, - makerTokenFillAmount - ); - } else if (assetDataId == ERC721_DATA_ID) { - totalFillResults = batchBuyERC721TokensInternal( - orders, - signatures, - feeOrders, - feeSignatures - ); - } - // Prevent accidental WETH owned by this contract and it being spent + // Ensure that no extra WETH owned by this contract has been sold. + uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount); require( - takerEthAmount >= totalFillResults.takerAssetFilledAmount, - "INVALID_MSG_VALUE" + totalWethSold <= msg.value, + "OVERSOLD_WETH" ); - withdrawPayAndDeductEthFee( - safeSub(takerEthAmount, totalFillResults.takerAssetFilledAmount), - totalFillResults.takerAssetFilledAmount, - feeProportion, + + // Transfer feePercentage of total ETH spent on primary orders to feeRecipient. + // Refund remaining ETH to msg.sender. + transferEthFeeAndRefund( + orderFillResults.takerAssetFilledAmount, + feeOrderFillResults.takerAssetFilledAmount, + feePercentage, feeRecipient ); - return totalFillResults; + + // Transfer purchased assets to msg.sender. + transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount); } - /// @dev Market sells WETH for ERC20 tokens. - /// @param orders An array of Order struct containing order specifications. - /// @param signatures An array of Proof that order has been created by maker. - /// @param feeOrders An array of Order struct containing order specifications for fees. - /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders. - /// @param wethSellAmount The amount of WETH to sell. - /// @return FillResults amounts filled and fees paid by maker and taker. - function marketSellEthForERC20Internal( + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param wethSellAmount Desired amount of WETH to sell. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by maker and taker. + function marketSellEth( LibOrder.Order[] memory orders, - bytes[] memory signatures, - LibOrder.Order[] memory feeOrders, - bytes[] memory feeSignatures, - uint256 wethSellAmount + uint256 wethSellAmount, + bytes[] memory signatures ) internal - returns (FillResults memory totalFillResults) + returns (FillResults memory fillResults) { - uint256 remainingWethSellAmount = wethSellAmount; - FillResults memory calculatedMarketSellResults = calculateMarketSellResults(orders, wethSellAmount); - if (calculatedMarketSellResults.takerFeePaid > 0) { - // Fees are required for these orders. Buy enough ZRX to cover the future market buy - FillResults memory feeTokensResults = marketBuyZrxInternal( - feeOrders, - feeSignatures, - calculatedMarketSellResults.takerFeePaid - ); - // Ensure the token abstraction was fair if fees were proportionally too high, we fail - require( - isAcceptableThreshold( - wethSellAmount, - safeSub(wethSellAmount, feeTokensResults.takerAssetFilledAmount) - ), - "UNACCEPTABLE_THRESHOLD" - ); - remainingWethSellAmount = safeSub(remainingWethSellAmount, feeTokensResults.takerAssetFilledAmount); - totalFillResults.takerFeePaid = feeTokensResults.takerFeePaid; - totalFillResults.takerAssetFilledAmount = feeTokensResults.takerAssetFilledAmount; + // `marketSellOrders` uses the first order's takerAssetData for all passed in orders. + orders[0].takerAssetData = WETH_ASSET_DATA; + + // All orders are required to have the same makerAssetData. We save on gas by reusing the makerAssetData of the first order. + for (uint256 i = 0; i < orders.length; i++) { + orders[i].makerAssetData = orders[0].makerAssetData; } - // Make our market sell to buy the requested tokens with the remaining balance - FillResults memory requestedTokensResults = EXCHANGE.marketSellOrders( + + // Sell WETH until entire amount has been sold or all orders have been filled. + fillResults = EXCHANGE.marketSellOrdersNoThrow( orders, - remainingWethSellAmount, + wethSellAmount, signatures ); - // Update our return FillResult with the market sell - addFillResults(totalFillResults, requestedTokensResults); - return totalFillResults; + + return fillResults; } - /// @dev Market sells WETH for ZRX tokens. - /// @param orders An array of Order struct containing order specifications. - /// @param signatures An array of Proof that order has been created by maker. - /// @param wethSellAmount The amount of WETH to sell. - /// @return FillResults amounts filled and fees paid by maker and taker. - function marketSellEthForZRXInternal( + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param makerAssetFillAmount Desired amount of makerAsset to buy. + /// @param signatures Proofs that orders have been created by makers. + /// @return Amounts filled and fees paid by maker and taker. + function marketBuyAsset( LibOrder.Order[] memory orders, - bytes[] memory signatures, - uint256 wethSellAmount + uint256 makerAssetFillAmount, + bytes[] memory signatures ) internal - returns (FillResults memory totalFillResults) + returns (FillResults memory fillResults) { - // Make our market sell to buy the requested tokens with the remaining balance - totalFillResults = EXCHANGE.marketSellOrders( + bytes memory wethAssetData = WETH_ASSET_DATA; + + // All orders are required to have WETH as takerAssetData. We save on gas by populating the orders here, rather than passing in any extra calldata. + for (uint256 i = 0; i < orders.length; i++) { + orders[i].takerAssetData = wethAssetData; + } + + // Purchase makerAsset until entire amount has been bought or all orders have been filled. + fillResults = EXCHANGE.marketBuyOrdersNoThrow( orders, - wethSellAmount, + makerAssetFillAmount, signatures ); - // Exchange does not special case ZRX in the makerAssetFilledAmount, if fees were deducted then using this amount - // for future transfers is invalid. - uint256 zrxAmountBought = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid); - require( - isAcceptableThreshold(totalFillResults.makerAssetFilledAmount, zrxAmountBought), - "UNACCEPTABLE_THRESHOLD" - ); - totalFillResults.makerAssetFilledAmount = zrxAmountBought; - return totalFillResults; + + return fillResults; } - /// @dev Buys an exact amount of an ERC20 token using WETH. - /// @param orders Orders to fill. The maker asset is the ERC20 token to buy. The taker asset is WETH. - /// @param signatures Proof that the orders were created by their respective makers. - /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH. - /// @param feeSignatures Proof that the feeOrders were created by their respective makers. - /// @param makerTokenFillAmount Amount of the ERC20 token to buy. - /// @return totalFillResults Aggregated fill results of buying the ERC20 and ZRX tokens. - function marketBuyERC20TokensInternal( + /// @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. + /// @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 marketBuyZrx( LibOrder.Order[] memory orders, - bytes[] memory signatures, - LibOrder.Order[] memory feeOrders, - bytes[] memory feeSignatures, - uint256 makerTokenFillAmount + uint256 zrxBuyAmount, + bytes[] memory signatures ) internal - returns (LibFillResults.FillResults memory totalFillResults) + returns (FillResults memory totalFillResults) { - // We read the maker token address to check if it is ZRX and later use it for transfer - address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16); - // We assume that asset being bought by taker is the same for each order. - // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders. - orders[0].takerAssetData = WETH_ASSET_DATA; - // We can short cut here for effeciency and use buyFeeTokensInternal if maker asset token is ZRX - // this buys us exactly that amount taking into account the fees. This saves gas and calculates the rate correctly - FillResults memory marketBuyResults; - if (makerTokenAddress == address(ZRX_TOKEN)) { - marketBuyResults = marketBuyZrxInternal( - orders, - signatures, - makerTokenFillAmount - ); - // When buying ZRX we round up which can result in a small margin excess - require( - marketBuyResults.makerAssetFilledAmount >= makerTokenFillAmount, - "UNACCEPTABLE_THRESHOLD" - ); - addFillResults(totalFillResults, marketBuyResults); - require( - isAcceptableThreshold( - safeAdd(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid), // Total ZRX - totalFillResults.makerAssetFilledAmount // amount going to msg.sender - ), - "UNACCEPTABLE_THRESHOLD" - ); - } else { - FillResults memory calculatedMarketBuyResults = calculateMarketBuyResults(orders, makerTokenFillAmount); - if (calculatedMarketBuyResults.takerFeePaid > 0) { - // Fees are required for these orders. Buy enough ZRX to cover the future market buy - FillResults memory zrxMarketBuyResults = marketBuyZrxInternal( - feeOrders, - feeSignatures, - calculatedMarketBuyResults.takerFeePaid + // Do nothing if zrxBuyAmount == 0 + if (zrxBuyAmount > 0) { + + bytes memory zrxAssetData = ZRX_ASSET_DATA; + bytes memory wethAssetData = WETH_ASSET_DATA; + + for (uint256 i = 0; i < orders.length; 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 = safeAdd( + safeSub(zrxBuyAmount, totalFillResults.makerAssetFilledAmount), + totalFillResults.takerFeePaid + ); + + // 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 = EXCHANGE.fillOrderNoThrow( + orders[i], + remainingWethSellAmount, + signatures[i] ); - totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount; - totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid; + + // Update amounts filled and fees paid by maker and taker. + addFillResults(totalFillResults, singleFillResult); + + // Stop execution if the entire amount of ZRX has been bought. + if (totalFillResults.makerAssetFilledAmount == zrxBuyAmount) { + break; + } } - // Make our market buy of the requested tokens with the remaining balance - marketBuyResults = EXCHANGE.marketBuyOrders( - orders, - makerTokenFillAmount, - signatures - ); - require( - marketBuyResults.makerAssetFilledAmount == makerTokenFillAmount, - "UNACCEPTABLE_THRESHOLD" - ); - addFillResults(totalFillResults, marketBuyResults); - require( - isAcceptableThreshold( - totalFillResults.takerAssetFilledAmount, - marketBuyResults.takerAssetFilledAmount - ), - "UNACCEPTABLE_THRESHOLD" - ); - } - // Transfer all purchased tokens to msg.sender - transferERC20Token( - makerTokenAddress, - msg.sender, - marketBuyResults.makerAssetFilledAmount - ); - return totalFillResults; - } - /// @dev Buys an all of the ERC721 tokens in the orders. - /// @param orders Orders to fill. The maker asset is the ERC721 token to buy. The taker asset is WETH. - /// @param signatures Proof that the orders were created by their respective makers. - /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH. - /// @param feeSignatures Proof that the feeOrders were created by their respective makers. - /// @return totalFillResults Aggregated fill results of buying the ERC721 tokens and ZRX tokens. - function batchBuyERC721TokensInternal( - LibOrder.Order[] memory orders, - bytes[] memory signatures, - LibOrder.Order[] memory feeOrders, - bytes[] memory feeSignatures - ) - internal - returns (LibFillResults.FillResults memory totalFillResults) - { - uint256 totalZrxFeeAmount; - uint256 ordersLength = orders.length; - uint256[] memory takerAssetFillAmounts = new uint256[](ordersLength); - for (uint256 i = 0; i < ordersLength; i++) { - // Total up the fees - totalZrxFeeAmount = safeAdd(totalZrxFeeAmount, orders[i].takerFee); - // We assume that asset being bought by taker is the same for each order. - // Rather than passing this in as calldata, we set the takerAssetData as WETH asset data - orders[i].takerAssetData = WETH_ASSET_DATA; - // Populate takerAssetFillAmounts for later batchFill - takerAssetFillAmounts[i] = orders[i].takerAssetAmount; - } - if (totalZrxFeeAmount > 0) { - // Fees are required for these orders. Buy enough ZRX to cover the future fill - FillResults memory zrxMarketBuyResults = marketBuyZrxInternal( - feeOrders, - feeSignatures, - totalZrxFeeAmount - ); - totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid; - totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount; - } - FillResults memory batchFillResults = EXCHANGE.batchFillOrKillOrders( - orders, - takerAssetFillAmounts, - signatures - ); - addFillResults(totalFillResults, batchFillResults); - require( - isAcceptableThreshold( - totalFillResults.takerAssetFilledAmount, - batchFillResults.takerAssetFilledAmount - ), - "UNACCEPTABLE_THRESHOLD" - ); - // Transfer all of the tokens filled from the batchFill - for (i = 0; i < ordersLength; i++) { - transferERC721Token( - orders[i].makerAssetData, - msg.sender + // Ensure that all ZRX spent while filling primary orders has been repurchased. + require( + totalFillResults.makerAssetFilledAmount == zrxBuyAmount, + "COMPLETE_FILL_FAILED" ); } return totalFillResults; |