diff options
6 files changed, 44 insertions, 141 deletions
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol index f3aa483c5..4584bb840 100644 --- a/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol @@ -139,7 +139,7 @@ contract MixinExchangeWrapper is /// @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 marketBuyWithWeth( + function marketBuyExactAmountWithWeth( LibOrder.Order[] memory orders, uint256 makerAssetFillAmount, bytes[] memory signatures @@ -180,10 +180,16 @@ contract MixinExchangeWrapper is addFillResults(totalFillResults, singleFillResults); // Stop execution if the entire amount of makerAsset has been bought - if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) { + uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount; + if (makerAssetFilledAmount >= makerAssetFillAmount) { break; } } + + require( + makerAssetFilledAmount >= makerAssetFillAmount, + "COMPLETE_FILL_FAILED" + ); return totalFillResults; } @@ -196,7 +202,7 @@ contract MixinExchangeWrapper is /// @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 marketBuyZrxWithWeth( + function marketBuyExactZrxWithWeth( LibOrder.Order[] memory orders, uint256 zrxBuyAmount, bytes[] memory signatures @@ -248,6 +254,10 @@ contract MixinExchangeWrapper is } } + require( + zrxPurchased >= zrxBuyAmount, + "COMPLETE_FILL_FAILED" + ); return totalFillResults; } } diff --git a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol index 1164ae919..0d313ea91 100644 --- a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol +++ b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol @@ -23,7 +23,7 @@ import "./libs/LibConstants.sol"; import "./mixins/MWeth.sol"; import "./mixins/MAssets.sol"; import "./mixins/MExchangeWrapper.sol"; -import "./mixins/MForwarderCore.sol"; +import "./interfaces/IForwarderCore.sol"; import "../utils/LibBytes/LibBytes.sol"; import "../protocol/Exchange/libs/LibOrder.sol"; import "../protocol/Exchange/libs/LibFillResults.sol"; @@ -37,7 +37,7 @@ contract MixinForwarderCore is MWeth, MAssets, MExchangeWrapper, - MForwarderCore + IForwarderCore { using LibBytes for bytes; @@ -117,7 +117,7 @@ contract MixinForwarderCore is ); // Buy back all ZRX spent on fees. zrxBuyAmount = orderFillResults.takerFeePaid; - feeOrderFillResults = marketBuyZrxWithWeth( + feeOrderFillResults = marketBuyExactZrxWithWeth( feeOrders, zrxBuyAmount, feeSignatures @@ -125,13 +125,6 @@ contract MixinForwarderCore is makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount; } - // Ensure that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold. - assertValidFillResults( - orderFillResults, - feeOrderFillResults, - zrxBuyAmount - ); - // Transfer feePercentage of total ETH spent on primary orders to feeRecipient. // Refund remaining ETH to msg.sender. transferEthFeeAndRefund( @@ -180,7 +173,7 @@ contract MixinForwarderCore is 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 = marketBuyZrxWithWeth( + orderFillResults = marketBuyExactZrxWithWeth( orders, makerAssetFillAmount, signatures @@ -190,14 +183,14 @@ contract MixinForwarderCore is } else { // Attemp to purchase desired amount of makerAsset. // ZRX fees are payed with this contract's balance. - orderFillResults = marketBuyWithWeth( + orderFillResults = marketBuyExactAmountWithWeth( orders, makerAssetFillAmount, signatures ); // Buy back all ZRX spent on fees. zrxBuyAmount = orderFillResults.takerFeePaid; - feeOrderFillResults = marketBuyZrxWithWeth( + feeOrderFillResults = marketBuyExactZrxWithWeth( feeOrders, zrxBuyAmount, feeSignatures @@ -205,13 +198,6 @@ contract MixinForwarderCore is makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount; } - // Ensure that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold. - assertValidFillResults( - orderFillResults, - feeOrderFillResults, - zrxBuyAmount - ); - // Transfer feePercentage of total ETH spent on primary orders to feeRecipient. // Refund remaining ETH to msg.sender. transferEthFeeAndRefund( @@ -224,31 +210,4 @@ contract MixinForwarderCore is // Transfer purchased assets to msg.sender. transferPurchasedAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased); } - - /// @dev Ensures that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold. - /// @param orderFillResults Amounts filled and fees paid for primary orders. - /// @param feeOrderFillResults Amounts filled and fees paid for fee orders. - /// @param zrxBuyAmount The amount of ZRX that needed to be repurchased after filling primary orders. - function assertValidFillResults( - FillResults memory orderFillResults, - FillResults memory feeOrderFillResults, - uint256 zrxBuyAmount - ) - internal - view - { - // Ensure that all ZRX spent while filling primary orders has been repurchased. - uint256 zrxPurchased = safeSub(feeOrderFillResults.makerAssetFilledAmount, feeOrderFillResults.takerFeePaid); - require( - zrxPurchased >= zrxBuyAmount, - "COMPLETE_FILL_FAILED" - ); - - // Ensure that no extra WETH owned by this contract has been sold. - uint256 wethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount); - require( - wethSold <= msg.value, - "OVERSOLD_WETH" - ); - } } diff --git a/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol b/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol index 8ba236e7f..e07940776 100644 --- a/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol +++ b/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol @@ -71,12 +71,16 @@ contract MixinWeth is "FEE_PERCENTAGE_TOO_LARGE" ); - // Calculate amount of WETH that hasn't been sold. - uint256 wethRemaining = safeSub( - msg.value, - safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx) + // 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, diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol index 5a2def7e5..360dea0e4 100644 --- a/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol +++ b/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol @@ -60,7 +60,7 @@ contract MExchangeWrapper { /// @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 marketBuyWithWeth( + function marketBuyExactAmountWithWeth( LibOrder.Order[] memory orders, uint256 makerAssetFillAmount, bytes[] memory signatures @@ -77,7 +77,7 @@ contract MExchangeWrapper { /// @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 marketBuyZrxWithWeth( + function marketBuyExactZrxWithWeth( LibOrder.Order[] memory orders, uint256 zrxBuyAmount, bytes[] memory signatures diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol deleted file mode 100644 index 0f5cd9c66..000000000 --- a/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol +++ /dev/null @@ -1,42 +0,0 @@ -/* - - 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"; -import "../interfaces/IForwarderCore.sol"; - - -contract MForwarderCore is - IForwarderCore -{ - - /// @dev Ensures that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold. - /// @param orderFillResults Amounts filled and fees paid for primary orders. - /// @param feeOrderFillResults Amounts filled and fees paid for fee orders. - /// @param zrxBuyAmount The amount of ZRX that needed to be repurchased after filling primary orders. - function assertValidFillResults( - LibFillResults.FillResults memory orderFillResults, - LibFillResults.FillResults memory feeOrderFillResults, - uint256 zrxBuyAmount - ) - internal - view; -} diff --git a/packages/contracts/test/forwarder/forwarder.ts b/packages/contracts/test/forwarder/forwarder.ts index 19639d3aa..cd7ae59c2 100644 --- a/packages/contracts/test/forwarder/forwarder.ts +++ b/packages/contracts/test/forwarder/forwarder.ts @@ -722,25 +722,18 @@ describe(ContractName.Forwarder, () => { ); expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); }); - it('should not change balances if the amount of ETH sent is too low to fill the makerAssetAmount', async () => { + it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => { const ordersWithoutFee = [orderWithoutFee]; const feeOrders: SignedOrder[] = []; const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2); const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(4); - - tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { - value: ethValue, - from: takerAddress, - }); - const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); - const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address); - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const totalEthSpent = gasPrice.times(tx.gasUsed); - - expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); - expect(newBalances).to.deep.equal(erc20Balances); - expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); + return expectTransactionFailedAsync( + forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { + value: ethValue, + from: takerAddress, + }), + RevertReason.CompleteFillFailed, + ); }); it('should buy an ERC721 asset from a single order', async () => { const makerAssetId = erc721MakerAssetIds[0]; @@ -775,7 +768,7 @@ describe(ContractName.Forwarder, () => { ); expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); }); - it('should buy an ERC721 asset and ignore later orders with different makerAssetData', async () => { + it('should revert if buying an ERC721 asset when later orders contain different makerAssetData', async () => { const makerAssetId = erc721MakerAssetIds[0]; orderWithoutFee = await orderFactory.newSignedOrderAsync({ makerAssetAmount: new BigNumber(1), @@ -786,33 +779,12 @@ describe(ContractName.Forwarder, () => { const feeOrders: SignedOrder[] = []; const makerAssetFillAmount = new BigNumber(1).plus(differentMakerAssetDataOrder.makerAssetAmount); const ethValue = orderWithFee.takerAssetAmount; - - tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { - from: takerAddress, - value: ethValue, - }); - const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress); - const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address); - const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId); - const newBalances = await erc20Wrapper.getBalancesAsync(); - - const primaryTakerAssetFillAmount = ethValue; - const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed)); - expect(newOwner).to.be.bignumber.equal(takerAddress); - expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent)); - expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount), - ); - expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT); - expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal( - constants.ZERO_AMOUNT, - ); - expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); - expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[makerAddress][defaultMakerAssetAddress], - ); - expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[takerAddress][defaultMakerAssetAddress], + return expectTransactionFailedAsync( + forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, { + value: ethValue, + from: takerAddress, + }), + RevertReason.CompleteFillFailed, ); }); it('should buy an ERC721 asset and pay ZRX fees from a single fee order', async () => { |