diff --git a/contracts/broker/package.json b/contracts/broker/package.json index 53b87b9945..daf353b1e6 100644 --- a/contracts/broker/package.json +++ b/contracts/broker/package.json @@ -52,6 +52,8 @@ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md", "devDependencies": { "@0x/abi-gen": "^5.1.0", + "@0x/contracts-asset-proxy": "^3.1.1", + "@0x/contracts-erc20": "^3.0.4", "@0x/contracts-erc721": "^3.0.4", "@0x/contracts-exchange": "^3.1.0", "@0x/contracts-exchange-libs": "^4.1.0", diff --git a/contracts/broker/src/gods_unchained_utils.ts b/contracts/broker/src/gods_unchained_utils.ts index 3094df186b..b1e2494954 100644 --- a/contracts/broker/src/gods_unchained_utils.ts +++ b/contracts/broker/src/gods_unchained_utils.ts @@ -13,20 +13,22 @@ export const godsUnchainedUtils = { }, /** * Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData - * of a property-based GodsUnchained order. Must also provide the address of the Broker and - * the GodsUnchainedValidator contract. The optional bundleSize parameter specifies how many - * cards are expected for each "unit" of the takerAssetAmount. For example, If the + * of a property-based GodsUnchained order. Must also provide the addresses of the Broker, + * GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies + * how many cards are expected for each "unit" of the takerAssetAmount. For example, If the * takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards * with the given proto and quality to fill the order. If an odd number is provided, the fill fails. */ encodeBrokerAssetData( brokerAddress: string, + godsUnchainedAddress: string, validatorAddress: string, proto: BigNumber, quality: BigNumber, bundleSize: number = 1, ): string { const dataEncoder = AbiEncoder.create([ + { name: 'godsUnchainedAddress', type: 'address' }, { name: 'validatorAddress', type: 'address' }, { name: 'propertyData', type: 'bytes' }, ]); @@ -34,7 +36,7 @@ export const godsUnchainedUtils = { { name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }, ]).encode({ proto, quality }); - const data = dataEncoder.encode({ validatorAddress, propertyData }); + const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData }); return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data); }, }; diff --git a/contracts/broker/test/gods_unchained_validator_test.ts b/contracts/broker/test/gods_unchained_validator_test.ts index cfadcc795c..e19e0a9986 100644 --- a/contracts/broker/test/gods_unchained_validator_test.ts +++ b/contracts/broker/test/gods_unchained_validator_test.ts @@ -1,5 +1,4 @@ -import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils'; -import { assetDataUtils } from '@0x/order-utils'; +import { blockchainTests, constants, expect, getRandomInteger } from '@0x/contracts-test-utils'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -39,28 +38,18 @@ blockchainTests.resets('GodsUnchainedValidator unit tests', env => { it('succeeds if assetData proto and quality match propertyData', async () => { const tokenId = getRandomInteger(0, constants.MAX_UINT256); await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync(); - const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId); - await validator.checkBrokerAsset(assetData, propertyData).callAsync(); - }); - it('reverts if assetData tokenAddress is not the GU contract address', async () => { - const tokenId = getRandomInteger(0, constants.MAX_UINT256); - await godsUnchained.setTokenProperties(tokenId, proto, quality).awaitTransactionSuccessAsync(); - const assetData = assetDataUtils.encodeERC721AssetData(randomAddress(), tokenId); - const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync(); - expect(tx).to.revertWith('TOKEN_ADDRESS_MISMATCH'); + await validator.checkBrokerAsset(tokenId, propertyData).callAsync(); }); it("reverts if assetData proto doesn't match propertyData", async () => { const tokenId = getRandomInteger(0, constants.MAX_UINT256); await godsUnchained.setTokenProperties(tokenId, proto.plus(1), quality).awaitTransactionSuccessAsync(); - const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId); - const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync(); + const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync(); expect(tx).to.revertWith('PROTO_MISMATCH'); }); it("reverts if assetData quality doesn't match proeprtyData", async () => { const tokenId = getRandomInteger(0, constants.MAX_UINT256); await godsUnchained.setTokenProperties(tokenId, proto, quality.plus(1)).awaitTransactionSuccessAsync(); - const assetData = assetDataUtils.encodeERC721AssetData(godsUnchained.address, tokenId); - const tx = validator.checkBrokerAsset(assetData, propertyData).callAsync(); + const tx = validator.checkBrokerAsset(tokenId, propertyData).callAsync(); expect(tx).to.revertWith('QUALITY_MISMATCH'); }); }); diff --git a/contracts/dev-utils/README.md b/contracts/dev-utils/README.md index 4f1f1ed69d..05ac1aee92 100644 --- a/contracts/dev-utils/README.md +++ b/contracts/dev-utils/README.md @@ -41,13 +41,13 @@ yarn install To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory: ```bash -PKG=@0x/contracts-extensions yarn build +PKG=@0x/contracts-dev-utils yarn build ``` Or continuously rebuild on change: ```bash -PKG=@0x/contracts-extensions yarn watch +PKG=@0x/contracts-dev-utils yarn watch ``` ### Clean diff --git a/contracts/exchange-forwarder/CHANGELOG.json b/contracts/exchange-forwarder/CHANGELOG.json index 146fe0b0ad..ee5ea44388 100644 --- a/contracts/exchange-forwarder/CHANGELOG.json +++ b/contracts/exchange-forwarder/CHANGELOG.json @@ -3,7 +3,7 @@ "version": "4.1.0", "changes": [ { - "note": "Moved LibAssetDataTransfer to exchange-libs", + "note": "Refactor, moved LibAssetDataTransfer and MixinWeth(Utils) to extensions", "pr": 2455 } ] diff --git a/contracts/exchange-forwarder/contracts/src/Forwarder.sol b/contracts/exchange-forwarder/contracts/src/Forwarder.sol index f841bdd7f2..ecac42715e 100644 --- a/contracts/exchange-forwarder/contracts/src/Forwarder.sol +++ b/contracts/exchange-forwarder/contracts/src/Forwarder.sol @@ -19,19 +19,32 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; -import "./MixinForwarderCore.sol"; -import "./libs/LibConstants.sol"; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; +import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol"; +import "@0x/contracts-extensions/contracts/src/MixinWethUtils.sol"; +import "@0x/contracts-utils/contracts/src/LibBytes.sol"; +import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; +import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; +import "@0x/contracts-utils/contracts/src/Ownable.sol"; +import "./libs/LibForwarderRichErrors.sol"; +import "./MixinExchangeWrapper.sol"; import "./MixinReceiver.sol"; +import "./interfaces/IForwarder.sol"; -// solhint-disable no-empty-blocks -// MixinAssets, MixinExchangeWrapper, and MixinWeth are all inherited via -// MixinForwarderCore. contract Forwarder is - LibConstants, - MixinForwarderCore, + IForwarder, + Ownable, + MixinWethUtils, + MixinExchangeWrapper, MixinReceiver { + using LibBytes for bytes; + using LibAssetDataTransfer for bytes; + using LibSafeMath for uint256; + constructor ( address _exchange, address _exchangeV2, @@ -39,11 +52,158 @@ contract Forwarder is ) public Ownable() - LibConstants( + MixinWethUtils( _exchange, - _exchangeV2, _weth ) - MixinForwarderCore() - {} + MixinExchangeWrapper( + _exchange, + _exchangeV2 + ) + {} // solhint-disable-line no-empty-blocks + + /// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets + /// that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of the asset to withdraw. + function withdrawAsset( + bytes calldata assetData, + uint256 amount + ) + external + onlyOwner + { + assetData.transferOut(amount); + } + + /// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf. + /// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the + /// Forwarder contract to the fee recipient. + /// This method needs to be called before forwarding orders of a maker asset that hasn't + /// previously been approved. + /// @param assetData Byte array encoded for the respective asset proxy. + function approveMakerAssetProxy(bytes calldata assetData) + external + { + bytes4 proxyId = assetData.readBytes4(0); + bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector; + + // For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid. + if (proxyId == erc20ProxyId) { + address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId); + if (proxyAddress == address(0)) { + LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError()); + } + address token = assetData.readAddress(16); + LibERC20Token.approve(token, proxyAddress, MAX_UINT256); + } + } + + /// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent + /// as possible, accounting for order and forwarder fees. + /// @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 ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. + /// @param feeRecipients Addresses that will receive ETH when orders are filled. + /// @return wethSpentAmount Amount of WETH spent on the given set of orders. + /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. + function marketSellOrdersWithEth( + LibOrder.Order[] memory orders, + bytes[] memory signatures, + uint256[] memory ethFeeAmounts, + address payable[] memory feeRecipients + ) + public + payable + returns ( + uint256 wethSpentAmount, + uint256 makerAssetAcquiredAmount + ) + { + // Pay ETH affiliate fees to all feeRecipient addresses + uint256 wethRemaining = _transferEthFeesAndWrapRemaining( + ethFeeAmounts, + feeRecipients + ); + // Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender, + // and pays WETH order fees. + ( + wethSpentAmount, + makerAssetAcquiredAmount + ) = _marketSellNoThrow( + orders, + wethRemaining, + signatures + ); + + // Ensure that no extra WETH owned by this contract has been spent. + if (wethSpentAmount > wethRemaining) { + LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError( + wethSpentAmount, + msg.value + )); + } + + // Calculate amount of WETH that hasn't been spent. + wethRemaining = wethRemaining.safeSub(wethSpentAmount); + + // Refund remaining ETH to msg.sender. + _transferEthRefund(wethRemaining); + } + + /// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction. + /// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can + /// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees). + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param makerAssetBuyAmount Desired amount of makerAsset to purchase. + /// @param signatures Proofs that orders have been created by makers. + /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. + /// @param feeRecipients Addresses that will receive ETH when orders are filled. + /// @return wethSpentAmount Amount of WETH spent on the given set of orders. + /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. + function marketBuyOrdersWithEth( + LibOrder.Order[] memory orders, + uint256 makerAssetBuyAmount, + bytes[] memory signatures, + uint256[] memory ethFeeAmounts, + address payable[] memory feeRecipients + ) + public + payable + returns ( + uint256 wethSpentAmount, + uint256 makerAssetAcquiredAmount + ) + { + // Pay ETH affiliate fees to all feeRecipient addresses + uint256 wethRemaining = _transferEthFeesAndWrapRemaining( + ethFeeAmounts, + feeRecipients + ); + + // Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender. + ( + wethSpentAmount, + makerAssetAcquiredAmount + ) = _marketBuyFillOrKill( + orders, + makerAssetBuyAmount, + signatures + ); + + // Ensure that no extra WETH owned by this contract has been spent. + if (wethSpentAmount > wethRemaining) { + LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError( + wethSpentAmount, + msg.value + )); + } + + // Calculate amount of WETH that hasn't been spent. + wethRemaining = wethRemaining.safeSub(wethSpentAmount); + + // Refund remaining ETH to msg.sender. + _transferEthRefund(wethRemaining); + } } diff --git a/contracts/exchange-forwarder/contracts/src/MixinAssets.sol b/contracts/exchange-forwarder/contracts/src/MixinAssets.sol deleted file mode 100644 index c7b0058fe8..0000000000 --- a/contracts/exchange-forwarder/contracts/src/MixinAssets.sol +++ /dev/null @@ -1,76 +0,0 @@ -/* - - Copyright 2019 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.5.9; - -import "@0x/contracts-exchange-libs/contracts/src/LibAssetDataTransfer.sol"; -import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; -import "@0x/contracts-utils/contracts/src/Ownable.sol"; -import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; -import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; -import "./libs/LibConstants.sol"; -import "./libs/LibForwarderRichErrors.sol"; -import "./interfaces/IAssets.sol"; - - -contract MixinAssets is - Ownable, - LibConstants, - IAssets -{ - using LibBytes for bytes; - using LibAssetDataTransfer for bytes; - - /// @dev Withdraws assets from this contract. It may be used by the owner to withdraw assets - /// that were accidentally sent to this contract. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param amount Amount of the asset to withdraw. - function withdrawAsset( - bytes calldata assetData, - uint256 amount - ) - external - onlyOwner - { - assetData.transferOut(amount); - } - - /// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf. - /// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the - /// Forwarder contract to the fee recipient. - /// This method needs to be called before forwarding orders of a maker asset that hasn't - /// previously been approved. - /// @param assetData Byte array encoded for the respective asset proxy. - function approveMakerAssetProxy(bytes calldata assetData) - external - { - bytes4 proxyId = assetData.readBytes4(0); - bytes4 erc20ProxyId = IAssetData(address(0)).ERC20Token.selector; - - // For now we only care about ERC20, since percentage fees on ERC721 tokens are invalid. - if (proxyId == erc20ProxyId) { - address proxyAddress = EXCHANGE.getAssetProxy(erc20ProxyId); - if (proxyAddress == address(0)) { - LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError()); - } - address token = assetData.readAddress(16); - LibERC20Token.approve(token, proxyAddress, MAX_UINT); - } - } -} diff --git a/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol b/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol index 9a8cd676e2..1a7e2519ae 100644 --- a/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol +++ b/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol @@ -19,28 +19,60 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; -import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; -import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; +import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol"; import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; -import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; -import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; -import "./libs/LibConstants.sol"; +import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol"; +import "@0x/contracts-utils/contracts/src/LibBytes.sol"; +import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; +import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "./libs/LibForwarderRichErrors.sol"; import "./interfaces/IExchangeV2.sol"; -import "./MixinAssets.sol"; -contract MixinExchangeWrapper is - LibConstants, - MixinAssets -{ +contract MixinExchangeWrapper { + + // The v2 order id is the first 4 bytes of the ExchangeV2 order schema hash. + // bytes4(keccak256(abi.encodePacked( + // "Order(", + // "address makerAddress,", + // "address takerAddress,", + // "address feeRecipientAddress,", + // "address senderAddress,", + // "uint256 makerAssetAmount,", + // "uint256 takerAssetAmount,", + // "uint256 makerFee,", + // "uint256 takerFee,", + // "uint256 expirationTimeSeconds,", + // "uint256 salt,", + // "bytes makerAssetData,", + // "bytes takerAssetData", + // ")" + // ))); + bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8; + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + IExchangeV2 internal EXCHANGE_V2; + // solhint-enable var-name-mixedcase + using LibBytes for bytes; + using LibAssetDataTransfer for bytes; using LibSafeMath for uint256; + constructor ( + address _exchange, + address _exchangeV2 + ) + public + { + EXCHANGE = IExchange(_exchange); + EXCHANGE_V2 = IExchangeV2(_exchangeV2); + } + /// @dev Fills the input order. /// Returns false if the transaction would otherwise revert. /// @param order Order struct containing order specifications. diff --git a/contracts/exchange-forwarder/contracts/src/MixinForwarderCore.sol b/contracts/exchange-forwarder/contracts/src/MixinForwarderCore.sol deleted file mode 100644 index ad12e5a34e..0000000000 --- a/contracts/exchange-forwarder/contracts/src/MixinForwarderCore.sol +++ /dev/null @@ -1,147 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; -import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol"; -import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; -import "./libs/LibConstants.sol"; -import "./libs/LibForwarderRichErrors.sol"; -import "./interfaces/IAssets.sol"; -import "./interfaces/IForwarderCore.sol"; -import "./MixinExchangeWrapper.sol"; -import "./MixinWeth.sol"; - - -contract MixinForwarderCore is - LibConstants, - IAssets, - IForwarderCore, - MixinWeth, - MixinExchangeWrapper -{ - using LibBytes for bytes; - using LibSafeMath for uint256; - - /// @dev Constructor approves ERC20 proxy to transfer WETH on this contract's behalf. - constructor () - public - { - address proxyAddress = EXCHANGE.getAssetProxy(IAssetData(address(0)).ERC20Token.selector); - if (proxyAddress == address(0)) { - LibRichErrors.rrevert(LibForwarderRichErrors.UnregisteredAssetProxyError()); - } - ETHER_TOKEN.approve(proxyAddress, MAX_UINT); - - address protocolFeeCollector = EXCHANGE.protocolFeeCollector(); - if (protocolFeeCollector != address(0)) { - ETHER_TOKEN.approve(protocolFeeCollector, MAX_UINT); - } - } - - /// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent - /// as possible, accounting for order and forwarder fees. - /// @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 ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. - /// @param feeRecipients Addresses that will receive ETH when orders are filled. - /// @return wethSpentAmount Amount of WETH spent on the given set of orders. - /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. - function marketSellOrdersWithEth( - LibOrder.Order[] memory orders, - bytes[] memory signatures, - uint256[] memory ethFeeAmounts, - address payable[] memory feeRecipients - ) - public - payable - returns ( - uint256 wethSpentAmount, - uint256 makerAssetAcquiredAmount - ) - { - // Pay ETH affiliate fees to all feeRecipient addresses - uint256 wethRemaining = _transferEthFeesAndWrapRemaining( - ethFeeAmounts, - feeRecipients - ); - // Spends up to wethRemaining to fill orders, transfers purchased assets to msg.sender, - // and pays WETH order fees. - ( - wethSpentAmount, - makerAssetAcquiredAmount - ) = _marketSellNoThrow( - orders, - wethRemaining, - signatures - ); - - // Refund remaining ETH to msg.sender. - _transferEthRefund(wethRemaining, wethSpentAmount); - } - - /// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction. - /// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can - /// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees). - /// Any ETH not spent will be refunded to sender. - /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. - /// @param makerAssetBuyAmount Desired amount of makerAsset to purchase. - /// @param signatures Proofs that orders have been created by makers. - /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. - /// @param feeRecipients Addresses that will receive ETH when orders are filled. - /// @return wethSpentAmount Amount of WETH spent on the given set of orders. - /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. - function marketBuyOrdersWithEth( - LibOrder.Order[] memory orders, - uint256 makerAssetBuyAmount, - bytes[] memory signatures, - uint256[] memory ethFeeAmounts, - address payable[] memory feeRecipients - ) - public - payable - returns ( - uint256 wethSpentAmount, - uint256 makerAssetAcquiredAmount - ) - { - // Pay ETH affiliate fees to all feeRecipient addresses - uint256 wethRemaining = _transferEthFeesAndWrapRemaining( - ethFeeAmounts, - feeRecipients - ); - - // Attempts to fill the desired amount of makerAsset and trasnfer purchased assets to msg.sender. - ( - wethSpentAmount, - makerAssetAcquiredAmount - ) = _marketBuyFillOrKill( - orders, - makerAssetBuyAmount, - signatures - ); - - // Refund remaining ETH to msg.sender. - _transferEthRefund(wethRemaining, wethSpentAmount); - } -} diff --git a/contracts/exchange-forwarder/contracts/src/interfaces/IAssets.sol b/contracts/exchange-forwarder/contracts/src/interfaces/IAssets.sol deleted file mode 100644 index 0c68952178..0000000000 --- a/contracts/exchange-forwarder/contracts/src/interfaces/IAssets.sol +++ /dev/null @@ -1,45 +0,0 @@ -/* - - Copyright 2019 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.5.9; - - -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 calldata assetData, - uint256 amount - ) - external; - - /// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf. - /// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the - /// Forwarder contract to the fee recipient. - /// This method needs to be called before forwarding orders of a maker asset that hasn't - /// previously been approved. - /// @param assetData Byte array encoded for the respective asset proxy. - function approveMakerAssetProxy( - bytes calldata assetData - ) - external; -} diff --git a/contracts/exchange-forwarder/contracts/src/interfaces/IForwarder.sol b/contracts/exchange-forwarder/contracts/src/interfaces/IForwarder.sol index fba646daf0..b288ddeec8 100644 --- a/contracts/exchange-forwarder/contracts/src/interfaces/IForwarder.sol +++ b/contracts/exchange-forwarder/contracts/src/interfaces/IForwarder.sol @@ -19,12 +19,77 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; -import "./IForwarderCore.sol"; -import "./IAssets.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; +import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol"; -// solhint-disable no-empty-blocks -contract IForwarder is - IForwarderCore, - IAssets -{} +contract IForwarder { + + /// @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 calldata assetData, + uint256 amount + ) + external; + + /// @dev Approves the respective proxy for a given asset to transfer tokens on the Forwarder contract's behalf. + /// This is necessary because an order fee denominated in the maker asset (i.e. a percentage fee) is sent by the + /// Forwarder contract to the fee recipient. + /// This method needs to be called before forwarding orders of a maker asset that hasn't + /// previously been approved. + /// @param assetData Byte array encoded for the respective asset proxy. + function approveMakerAssetProxy( + bytes calldata assetData + ) + external; + + /// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent + /// as possible, accounting for order and forwarder fees. + /// @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 ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. + /// @param feeRecipients Addresses that will receive ETH when orders are filled. + /// @return wethSpentAmount Amount of WETH spent on the given set of orders. + /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. + function marketSellOrdersWithEth( + LibOrder.Order[] memory orders, + bytes[] memory signatures, + uint256[] memory ethFeeAmounts, + address payable[] memory feeRecipients + ) + public + payable + returns ( + uint256 wethSpentAmount, + uint256 makerAssetAcquiredAmount + ); + + /// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction. + /// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can + /// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees). + /// Any ETH not spent will be refunded to sender. + /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. + /// @param makerAssetBuyAmount Desired amount of makerAsset to purchase. + /// @param signatures Proofs that orders have been created by makers. + /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. + /// @param feeRecipients Addresses that will receive ETH when orders are filled. + /// @return wethSpentAmount Amount of WETH spent on the given set of orders. + /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. + function marketBuyOrdersWithEth( + LibOrder.Order[] memory orders, + uint256 makerAssetBuyAmount, + bytes[] memory signatures, + uint256[] memory ethFeeAmounts, + address payable[] memory feeRecipients + ) + public + payable + returns ( + uint256 wethSpentAmount, + uint256 makerAssetAcquiredAmount + ); +} diff --git a/contracts/exchange-forwarder/contracts/src/interfaces/IForwarderCore.sol b/contracts/exchange-forwarder/contracts/src/interfaces/IForwarderCore.sol deleted file mode 100644 index f37da3f1ba..0000000000 --- a/contracts/exchange-forwarder/contracts/src/interfaces/IForwarderCore.sol +++ /dev/null @@ -1,73 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol"; - - -contract IForwarderCore { - - /// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent - /// as possible, accounting for order and forwarder fees. - /// @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 ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. - /// @param feeRecipients Addresses that will receive ETH when orders are filled. - /// @return wethSpentAmount Amount of WETH spent on the given set of orders. - /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. - function marketSellOrdersWithEth( - LibOrder.Order[] memory orders, - bytes[] memory signatures, - uint256[] memory ethFeeAmounts, - address payable[] memory feeRecipients - ) - public - payable - returns ( - uint256 wethSpentAmount, - uint256 makerAssetAcquiredAmount - ); - - /// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction. - /// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can - /// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees). - /// Any ETH not spent will be refunded to sender. - /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. - /// @param makerAssetBuyAmount Desired amount of makerAsset to purchase. - /// @param signatures Proofs that orders have been created by makers. - /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients. - /// @param feeRecipients Addresses that will receive ETH when orders are filled. - /// @return wethSpentAmount Amount of WETH spent on the given set of orders. - /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. - function marketBuyOrdersWithEth( - LibOrder.Order[] memory orders, - uint256 makerAssetBuyAmount, - bytes[] memory signatures, - uint256[] memory ethFeeAmounts, - address payable[] memory feeRecipients - ) - public - payable - returns ( - uint256 wethSpentAmount, - uint256 makerAssetAcquiredAmount - ); -} diff --git a/contracts/exchange-forwarder/contracts/src/libs/LibConstants.sol b/contracts/exchange-forwarder/contracts/src/libs/LibConstants.sol deleted file mode 100644 index 05fe8d2c73..0000000000 --- a/contracts/exchange-forwarder/contracts/src/libs/LibConstants.sol +++ /dev/null @@ -1,66 +0,0 @@ -/* - - Copyright 2019 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.5.9; - -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; -import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; -import "../interfaces/IExchangeV2.sol"; - - -contract LibConstants { - - uint256 constant internal MAX_UINT = uint256(-1); - - // The v2 order id is the first 4 bytes of the ExchangeV2 order schema hash. - // bytes4(keccak256(abi.encodePacked( - // "Order(", - // "address makerAddress,", - // "address takerAddress,", - // "address feeRecipientAddress,", - // "address senderAddress,", - // "uint256 makerAssetAmount,", - // "uint256 takerAssetAmount,", - // "uint256 makerFee,", - // "uint256 takerFee,", - // "uint256 expirationTimeSeconds,", - // "uint256 salt,", - // "bytes makerAssetData,", - // "bytes takerAssetData", - // ")" - // ))); - bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8; - - // solhint-disable var-name-mixedcase - IExchange internal EXCHANGE; - IExchangeV2 internal EXCHANGE_V2; - IEtherToken internal ETHER_TOKEN; - // solhint-enable var-name-mixedcase - - constructor ( - address _exchange, - address _exchangeV2, - address _weth - ) - public - { - EXCHANGE = IExchange(_exchange); - EXCHANGE_V2 = IExchangeV2(_exchangeV2); - ETHER_TOKEN = IEtherToken(_weth); - } -} diff --git a/contracts/exchange-forwarder/contracts/src/libs/LibForwarderRichErrors.sol b/contracts/exchange-forwarder/contracts/src/libs/LibForwarderRichErrors.sol index 2d6e57f780..9fc9d7c527 100644 --- a/contracts/exchange-forwarder/contracts/src/libs/LibForwarderRichErrors.sol +++ b/contracts/exchange-forwarder/contracts/src/libs/LibForwarderRichErrors.sol @@ -33,22 +33,10 @@ library LibForwarderRichErrors { bytes4 internal constant UNSUPPORTED_FEE_ERROR_SELECTOR = 0x31360af1; - // bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)")) - bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR = - 0xecf40fd9; - // bytes4(keccak256("OverspentWethError(uint256,uint256)")) bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR = 0xcdcbed5d; - // bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)")) - bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR = - 0x08b18698; - - // bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)")) - bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR = - 0x3ecb6ceb; - // solhint-disable func-name-mixedcase function UnregisteredAssetProxyError() internal @@ -86,21 +74,6 @@ library LibForwarderRichErrors { ); } - function InsufficientEthForFeeError( - uint256 ethFeeRequired, - uint256 ethAvailable - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR, - ethFeeRequired, - ethAvailable - ); - } - function OverspentWethError( uint256 wethSpent, uint256 msgValue @@ -115,32 +88,4 @@ library LibForwarderRichErrors { msgValue ); } - - function DefaultFunctionWethContractOnlyError( - address senderAddress - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR, - senderAddress - ); - } - - function EthFeeLengthMismatchError( - uint256 ethFeesLength, - uint256 feeRecipientsLength - ) - internal - pure - returns (bytes memory) - { - return abi.encodeWithSelector( - ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR, - ethFeesLength, - feeRecipientsLength - ); - } } diff --git a/contracts/exchange-forwarder/contracts/test/TestForwarder.sol b/contracts/exchange-forwarder/contracts/test/TestForwarder.sol index c6f9f45b3d..6447bce93e 100644 --- a/contracts/exchange-forwarder/contracts/test/TestForwarder.sol +++ b/contracts/exchange-forwarder/contracts/test/TestForwarder.sol @@ -19,14 +19,12 @@ pragma solidity ^0.5.9; pragma experimental ABIEncoderV2; -import "@0x/contracts-exchange-libs/contracts/src/LibAssetDataTransfer.sol"; +import "@0x/contracts-extensions/contracts/src/LibAssetDataTransfer.sol"; import "../src/MixinExchangeWrapper.sol"; -import "../src/libs/LibConstants.sol"; import "../src/MixinReceiver.sol"; contract TestForwarder is - LibConstants, MixinExchangeWrapper, MixinReceiver { @@ -35,8 +33,7 @@ contract TestForwarder is // solhint-disable no-empty-blocks constructor () public - LibConstants( - address(0), + MixinExchangeWrapper( address(0), address(0) ) diff --git a/contracts/exchange-forwarder/package.json b/contracts/exchange-forwarder/package.json index f35a5b4ad5..6befe99d58 100644 --- a/contracts/exchange-forwarder/package.json +++ b/contracts/exchange-forwarder/package.json @@ -39,7 +39,7 @@ }, "config": { "publicInterfaceContracts": "Forwarder,IExchangeV2", - "abis": "./test/generated-artifacts/@(Forwarder|IAssets|IExchangeV2|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinReceiver|MixinWeth|TestForwarder).json", + "abis": "./test/generated-artifacts/@(Forwarder|IExchangeV2|IForwarder|LibForwarderRichErrors|MixinExchangeWrapper|MixinReceiver|TestForwarder).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/exchange-forwarder/test/artifacts.ts b/contracts/exchange-forwarder/test/artifacts.ts index 89c80a983d..c4b14eccd3 100644 --- a/contracts/exchange-forwarder/test/artifacts.ts +++ b/contracts/exchange-forwarder/test/artifacts.ts @@ -6,30 +6,18 @@ import { ContractArtifact } from 'ethereum-types'; import * as Forwarder from '../test/generated-artifacts/Forwarder.json'; -import * as IAssets from '../test/generated-artifacts/IAssets.json'; import * as IExchangeV2 from '../test/generated-artifacts/IExchangeV2.json'; import * as IForwarder from '../test/generated-artifacts/IForwarder.json'; -import * as IForwarderCore from '../test/generated-artifacts/IForwarderCore.json'; -import * as LibConstants from '../test/generated-artifacts/LibConstants.json'; import * as LibForwarderRichErrors from '../test/generated-artifacts/LibForwarderRichErrors.json'; -import * as MixinAssets from '../test/generated-artifacts/MixinAssets.json'; import * as MixinExchangeWrapper from '../test/generated-artifacts/MixinExchangeWrapper.json'; -import * as MixinForwarderCore from '../test/generated-artifacts/MixinForwarderCore.json'; import * as MixinReceiver from '../test/generated-artifacts/MixinReceiver.json'; -import * as MixinWeth from '../test/generated-artifacts/MixinWeth.json'; import * as TestForwarder from '../test/generated-artifacts/TestForwarder.json'; export const artifacts = { Forwarder: Forwarder as ContractArtifact, - MixinAssets: MixinAssets as ContractArtifact, MixinExchangeWrapper: MixinExchangeWrapper as ContractArtifact, - MixinForwarderCore: MixinForwarderCore as ContractArtifact, MixinReceiver: MixinReceiver as ContractArtifact, - MixinWeth: MixinWeth as ContractArtifact, - IAssets: IAssets as ContractArtifact, IExchangeV2: IExchangeV2 as ContractArtifact, IForwarder: IForwarder as ContractArtifact, - IForwarderCore: IForwarderCore as ContractArtifact, - LibConstants: LibConstants as ContractArtifact, LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact, TestForwarder: TestForwarder as ContractArtifact, }; diff --git a/contracts/exchange-forwarder/test/wrappers.ts b/contracts/exchange-forwarder/test/wrappers.ts index be47b14fb8..4d0e984bd3 100644 --- a/contracts/exchange-forwarder/test/wrappers.ts +++ b/contracts/exchange-forwarder/test/wrappers.ts @@ -4,15 +4,9 @@ * ----------------------------------------------------------------------------- */ export * from '../test/generated-wrappers/forwarder'; -export * from '../test/generated-wrappers/i_assets'; export * from '../test/generated-wrappers/i_exchange_v2'; export * from '../test/generated-wrappers/i_forwarder'; -export * from '../test/generated-wrappers/i_forwarder_core'; -export * from '../test/generated-wrappers/lib_constants'; export * from '../test/generated-wrappers/lib_forwarder_rich_errors'; -export * from '../test/generated-wrappers/mixin_assets'; export * from '../test/generated-wrappers/mixin_exchange_wrapper'; -export * from '../test/generated-wrappers/mixin_forwarder_core'; export * from '../test/generated-wrappers/mixin_receiver'; -export * from '../test/generated-wrappers/mixin_weth'; export * from '../test/generated-wrappers/test_forwarder'; diff --git a/contracts/exchange-forwarder/tsconfig.json b/contracts/exchange-forwarder/tsconfig.json index 5953b5f196..8c17b92d88 100644 --- a/contracts/exchange-forwarder/tsconfig.json +++ b/contracts/exchange-forwarder/tsconfig.json @@ -6,17 +6,11 @@ "generated-artifacts/Forwarder.json", "generated-artifacts/IExchangeV2.json", "test/generated-artifacts/Forwarder.json", - "test/generated-artifacts/IAssets.json", "test/generated-artifacts/IExchangeV2.json", "test/generated-artifacts/IForwarder.json", - "test/generated-artifacts/IForwarderCore.json", - "test/generated-artifacts/LibConstants.json", "test/generated-artifacts/LibForwarderRichErrors.json", - "test/generated-artifacts/MixinAssets.json", "test/generated-artifacts/MixinExchangeWrapper.json", - "test/generated-artifacts/MixinForwarderCore.json", "test/generated-artifacts/MixinReceiver.json", - "test/generated-artifacts/MixinWeth.json", "test/generated-artifacts/TestForwarder.json" ], "exclude": ["./deploy/solc/solc_bin"] diff --git a/contracts/exchange-libs/package.json b/contracts/exchange-libs/package.json index 373bb4f929..af31197e08 100644 --- a/contracts/exchange-libs/package.json +++ b/contracts/exchange-libs/package.json @@ -39,7 +39,7 @@ }, "config": { "publicInterfaceContracts": "IWallet,LibEIP712ExchangeDomain,LibExchangeRichErrors,LibMath,LibMathRichErrors,LibOrder,LibZeroExTransaction", - "abis": "./test/generated-artifacts/@(IWallet|LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json", + "abis": "./test/generated-artifacts/@(IWallet|LibEIP712ExchangeDomain|LibExchangeRichErrors|LibFillResults|LibMath|LibMathRichErrors|LibOrder|LibZeroExTransaction|TestLibEIP712ExchangeDomain|TestLibFillResults|TestLibMath|TestLibOrder|TestLibZeroExTransaction).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/exchange-libs/src/index.ts b/contracts/exchange-libs/src/index.ts index 7df2c24ef1..f0fb0f8085 100644 --- a/contracts/exchange-libs/src/index.ts +++ b/contracts/exchange-libs/src/index.ts @@ -8,7 +8,7 @@ export { LibOrderContract, LibZeroExTransactionContract, } from './wrappers'; -export { LibAssetDataTransferRevertErrors, LibMathRevertErrors } from '@0x/utils'; +export { LibMathRevertErrors } from '@0x/utils'; import * as ReferenceFunctionsToExport from './reference_functions'; export import ReferenceFunctions = ReferenceFunctionsToExport; diff --git a/contracts/exchange-libs/test/artifacts.ts b/contracts/exchange-libs/test/artifacts.ts index bdd73abe70..01c47756ec 100644 --- a/contracts/exchange-libs/test/artifacts.ts +++ b/contracts/exchange-libs/test/artifacts.ts @@ -6,8 +6,6 @@ import { ContractArtifact } from 'ethereum-types'; import * as IWallet from '../test/generated-artifacts/IWallet.json'; -import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json'; -import * as LibAssetDataTransferRichErrors from '../test/generated-artifacts/LibAssetDataTransferRichErrors.json'; import * as LibEIP712ExchangeDomain from '../test/generated-artifacts/LibEIP712ExchangeDomain.json'; import * as LibExchangeRichErrors from '../test/generated-artifacts/LibExchangeRichErrors.json'; import * as LibFillResults from '../test/generated-artifacts/LibFillResults.json'; @@ -22,8 +20,6 @@ import * as TestLibOrder from '../test/generated-artifacts/TestLibOrder.json'; import * as TestLibZeroExTransaction from '../test/generated-artifacts/TestLibZeroExTransaction.json'; export const artifacts = { IWallet: IWallet as ContractArtifact, - LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact, - LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact, LibEIP712ExchangeDomain: LibEIP712ExchangeDomain as ContractArtifact, LibExchangeRichErrors: LibExchangeRichErrors as ContractArtifact, LibFillResults: LibFillResults as ContractArtifact, diff --git a/contracts/exchange-libs/test/wrappers.ts b/contracts/exchange-libs/test/wrappers.ts index 98042bd054..c0205dc234 100644 --- a/contracts/exchange-libs/test/wrappers.ts +++ b/contracts/exchange-libs/test/wrappers.ts @@ -4,8 +4,6 @@ * ----------------------------------------------------------------------------- */ export * from '../test/generated-wrappers/i_wallet'; -export * from '../test/generated-wrappers/lib_asset_data_transfer'; -export * from '../test/generated-wrappers/lib_asset_data_transfer_rich_errors'; export * from '../test/generated-wrappers/lib_e_i_p712_exchange_domain'; export * from '../test/generated-wrappers/lib_exchange_rich_errors'; export * from '../test/generated-wrappers/lib_fill_results'; diff --git a/contracts/exchange-libs/tsconfig.json b/contracts/exchange-libs/tsconfig.json index b32b8739d6..26f76e6e57 100644 --- a/contracts/exchange-libs/tsconfig.json +++ b/contracts/exchange-libs/tsconfig.json @@ -11,8 +11,6 @@ "generated-artifacts/LibOrder.json", "generated-artifacts/LibZeroExTransaction.json", "test/generated-artifacts/IWallet.json", - "test/generated-artifacts/LibAssetDataTransfer.json", - "test/generated-artifacts/LibAssetDataTransferRichErrors.json", "test/generated-artifacts/LibEIP712ExchangeDomain.json", "test/generated-artifacts/LibExchangeRichErrors.json", "test/generated-artifacts/LibFillResults.json", diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index 19b03d1277..ff26a511e7 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "6.0.0", + "changes": [ + { + "note": "New year, new me: remove everything, add MixinWethUtils and LibAssetDataTransfer", + "pr": 2455 + } + ] + }, { "timestamp": 1580811564, "version": "5.1.4", diff --git a/contracts/extensions/README.md b/contracts/extensions/README.md index f8772e2a25..9197920098 100644 --- a/contracts/extensions/README.md +++ b/contracts/extensions/README.md @@ -1,6 +1,6 @@ ## Extensions -This package implements various extensions to the 0x protocol. Extension contracts can add various rules around how orders are settled while still getting the interoperability and security benefits of using the underlying 0x protocol contracts. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package. +This package includes utility smart contracts that may be useful for developing on top of the core 0x Exchange. For example, `MixinWethUtils` includes functions to pay affiliate fees in ETH, wrap ETH to be used for 0x trades and protocol fees, and unwrap WETH to refund the sender. ## Installation @@ -12,7 +12,7 @@ npm install @0x/contracts-extensions --save ## Bug bounty -A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program). +A bug bounty for the 3.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program). ## Contributing diff --git a/contracts/extensions/contracts/src/BalanceThresholdFilter/BalanceThresholdFilter.sol b/contracts/extensions/contracts/src/BalanceThresholdFilter/BalanceThresholdFilter.sol deleted file mode 100644 index d4b8587318..0000000000 --- a/contracts/extensions/contracts/src/BalanceThresholdFilter/BalanceThresholdFilter.sol +++ /dev/null @@ -1,46 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; -import "./interfaces/IThresholdAsset.sol"; -import "./MixinBalanceThresholdFilterCore.sol"; - - -contract BalanceThresholdFilter is - MixinBalanceThresholdFilterCore -{ - - /// @dev Constructs BalanceThresholdFilter. - /// @param exchange Address of 0x exchange. - /// @param thresholdAsset The asset that must be held by makers/takers. - /// @param balanceThreshold The minimum balance of `thresholdAsset` that must be held by makers/takers. - constructor ( - address exchange, - address thresholdAsset, - uint256 balanceThreshold - ) - public - { - EXCHANGE = IExchange(exchange); - THRESHOLD_ASSET = IThresholdAsset(thresholdAsset); - BALANCE_THRESHOLD = balanceThreshold; - } -} diff --git a/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol deleted file mode 100644 index 68028cc07e..0000000000 --- a/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ /dev/null @@ -1,136 +0,0 @@ -/* - - Copyright 2019 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/LICENSE2.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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange-libs/contracts/src/LibZeroExTransaction.sol"; -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; -import "./MixinExchangeCalldata.sol"; -import "./interfaces/IBalanceThresholdFilterCore.sol"; - - -contract MixinBalanceThresholdFilterCore is - IBalanceThresholdFilterCore, - MixinExchangeCalldata -{ - - /// @dev Executes an Exchange transaction iff the maker and taker meet - /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR - /// the exchange function is a cancellation. - /// Supported Exchange functions: - /// batchFillOrders - /// batchFillOrdersNoThrow - /// batchFillOrKillOrders - /// fillOrder - /// fillOrderNoThrow - /// fillOrKillOrder - /// marketBuyOrders - /// marketBuyOrdersNoThrow - /// marketSellOrders - /// marketSellOrdersNoThrow - /// matchOrders - /// cancelOrder - /// batchCancelOrders - /// cancelOrdersUpTo - /// Trying to call any other exchange function will throw. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param signedExchangeTransaction AbiV2 encoded calldata. - /// @param signature Proof of signer transaction by signer. - function executeTransaction( - uint256 salt, - address signerAddress, - bytes calldata signedExchangeTransaction, - bytes calldata signature - ) - external - { - // Get accounts whose balances must be validated - address[] memory addressesToValidate = _getAddressesToValidate(signerAddress); - - // Validate account balances - uint256 balanceThreshold = BALANCE_THRESHOLD; - IThresholdAsset thresholdAsset = THRESHOLD_ASSET; - for (uint256 i = 0; i < addressesToValidate.length; ++i) { - uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); - require( - addressBalance >= balanceThreshold, - "AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD" - ); - } - emit ValidatedAddresses(addressesToValidate); - - LibZeroExTransaction.ZeroExTransaction memory transaction = LibZeroExTransaction.ZeroExTransaction({ - salt: salt, - data: signedExchangeTransaction, - signerAddress: signerAddress - }); - - // All addresses are valid. Execute exchange function. - EXCHANGE.executeTransaction(transaction, signature); - } - - /// @dev Constructs an array of addresses to be validated. - /// Addresses depend on which Exchange function is to be called - /// (defined by `signedExchangeTransaction` above). - /// @param signerAddress Address of transaction signer. - /// @return addressesToValidate Array of addresses to validate. - function _getAddressesToValidate(address signerAddress) - internal - pure - returns (address[] memory addressesToValidate) - { - bytes4 exchangeFunctionSelector = bytes4(_exchangeCalldataload(0)); - // solhint-disable expression-indent - if ( - exchangeFunctionSelector == IExchange(address(0)).batchFillOrders.selector || - exchangeFunctionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector || - exchangeFunctionSelector == IExchange(address(0)).batchFillOrKillOrders.selector || - exchangeFunctionSelector == IExchange(address(0)).marketBuyOrders.selector || - exchangeFunctionSelector == IExchange(address(0)).marketBuyOrdersNoThrow.selector || - exchangeFunctionSelector == IExchange(address(0)).marketSellOrders.selector || - exchangeFunctionSelector == IExchange(address(0)).marketSellOrdersNoThrow.selector - ) { - addressesToValidate = _loadMakerAddressesFromOrderArray(0); - addressesToValidate = addressesToValidate.append(signerAddress); - } else if ( - exchangeFunctionSelector == IExchange(address(0)).fillOrder.selector || - exchangeFunctionSelector == IExchange(address(0)).fillOrderNoThrow.selector || - exchangeFunctionSelector == IExchange(address(0)).fillOrKillOrder.selector - ) { - address makerAddress = _loadMakerAddressFromOrder(0); - addressesToValidate = addressesToValidate.append(makerAddress); - addressesToValidate = addressesToValidate.append(signerAddress); - } else if (exchangeFunctionSelector == IExchange(address(0)).matchOrders.selector) { - address leftMakerAddress = _loadMakerAddressFromOrder(0); - addressesToValidate = addressesToValidate.append(leftMakerAddress); - address rightMakerAddress = _loadMakerAddressFromOrder(1); - addressesToValidate = addressesToValidate.append(rightMakerAddress); - addressesToValidate = addressesToValidate.append(signerAddress); - } else if ( - exchangeFunctionSelector != IExchange(address(0)).cancelOrder.selector && - exchangeFunctionSelector != IExchange(address(0)).batchCancelOrders.selector && - exchangeFunctionSelector != IExchange(address(0)).cancelOrdersUpTo.selector - ) { - revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); - } - // solhint-enable expression-indent - return addressesToValidate; - } -} diff --git a/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinExchangeCalldata.sol b/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinExchangeCalldata.sol deleted file mode 100644 index 0fe0621f86..0000000000 --- a/contracts/extensions/contracts/src/BalanceThresholdFilter/MixinExchangeCalldata.sol +++ /dev/null @@ -1,104 +0,0 @@ - - /* - - Copyright 2019 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/LICENSE2.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.5.9; - -import "@0x/contracts-utils/contracts/src/LibAddressArray.sol"; - - -contract MixinExchangeCalldata { - - using LibAddressArray for address[]; - - /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, - /// which is accessed through `signedExchangeTransaction`. - /// @param offset Offset into the Exchange calldata. - /// @return value Corresponding 32 byte value stored at `offset`. - function _exchangeCalldataload(uint256 offset) - internal - pure - returns (bytes32 value) - { - assembly { - // Pointer to exchange transaction - // 0x04 for calldata selector - // 0x40 to access `signedExchangeTransaction`, which is the third parameter - let exchangeTxPtr := calldataload(0x44) - - // Offset into Exchange calldata - // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. - // 0x04 for calldata selector - // 0x20 for length field of `signedExchangeTransaction` - let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) - value := calldataload(exchangeCalldataOffset) - } - return value; - } - - /// @dev Convenience function that skips the 4 byte selector when loading - /// from the embedded Exchange calldata. - /// @param offset Offset into the Exchange calldata (minus the 4 byte selector) - /// @return value Corresponding 32 byte value stored at `offset` + 4. - function _loadExchangeData(uint256 offset) - internal - pure - returns (bytes32 value) - { - value = _exchangeCalldataload(offset + 4); - return value; - } - - /// @dev Extracts the maker address from an order stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`). - /// @param orderParamIndex Index of the order in the Exchange function's signature. - /// @return makerAddress The extracted maker address. - function _loadMakerAddressFromOrder(uint256 orderParamIndex) - internal - pure - returns (address makerAddress) - { - uint256 orderOffsetInBytes = orderParamIndex * 32; - uint256 orderPtr = uint256(_loadExchangeData(orderOffsetInBytes)); - makerAddress = address(uint256(_loadExchangeData(orderPtr))); - return makerAddress; - } - - /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`). - /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature - /// @return makerAddresses The extracted maker addresses. - function _loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex) - internal - pure - returns (address[] memory makerAddresses) - { - uint256 orderArrayOffsetInBytes = orderArrayParamIndex * 32; - uint256 orderArrayPtr = uint256(_loadExchangeData(orderArrayOffsetInBytes)); - uint256 orderArrayLength = uint256(_loadExchangeData(orderArrayPtr)); - uint256 orderArrayLengthInBytes = orderArrayLength * 32; - uint256 orderArrayElementPtr = orderArrayPtr + 32; - uint256 orderArrayElementEndPtr = orderArrayElementPtr + orderArrayLengthInBytes; - for (uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 32) { - uint256 orderPtr = uint256(_loadExchangeData(orderPtrOffset)); - address makerAddress = address(uint256(_loadExchangeData(orderPtr + orderArrayElementPtr))); - makerAddresses = makerAddresses.append(makerAddress); - } - return makerAddresses; - } -} diff --git a/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol deleted file mode 100644 index 842a8b8415..0000000000 --- a/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol +++ /dev/null @@ -1,55 +0,0 @@ - -/* - - Copyright 2019 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.5.9; - - -contract IBalanceThresholdFilterCore { - - /// @dev Executes an Exchange transaction iff the maker and taker meet - /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR - /// the exchange function is a cancellation. - /// Supported Exchange functions: - /// - batchFillOrders - /// - batchFillOrdersNoThrow - /// - batchFillOrKillOrders - /// - fillOrder - /// - fillOrderNoThrow - /// - fillOrKillOrder - /// - marketBuyOrders - /// - marketBuyOrdersNoThrow - /// - marketSellOrders - /// - marketSellOrdersNoThrow - /// - matchOrders - /// - cancelOrder - /// - batchCancelOrders - /// - cancelOrdersUpTo - /// Trying to call any other exchange function will throw. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. - /// @param signerAddress Address of transaction signer. - /// @param signedExchangeTransaction AbiV2 encoded calldata. - /// @param signature Proof of signer transaction by signer. - function executeTransaction( - uint256 salt, - address signerAddress, - bytes calldata signedExchangeTransaction, - bytes calldata signature - ) - external; -} diff --git a/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IThresholdAsset.sol b/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IThresholdAsset.sol deleted file mode 100644 index 062f81dc56..0000000000 --- a/contracts/extensions/contracts/src/BalanceThresholdFilter/interfaces/IThresholdAsset.sol +++ /dev/null @@ -1,31 +0,0 @@ -/* - - Copyright 2019 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.5.9; - - -contract IThresholdAsset { - - /// @param _owner The address from which the balance will be retrieved - /// @return Balance of owner - function balanceOf(address _owner) - external - view - returns (uint256); - -} diff --git a/contracts/extensions/contracts/src/DutchAuction/DutchAuction.sol b/contracts/extensions/contracts/src/DutchAuction/DutchAuction.sol deleted file mode 100644 index e8ddfaca64..0000000000 --- a/contracts/extensions/contracts/src/DutchAuction/DutchAuction.sol +++ /dev/null @@ -1,199 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; -import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; -import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; - - -contract DutchAuction { - - using LibBytes for bytes; - using LibSafeMath for uint256; - - // solhint-disable var-name-mixedcase - IExchange internal EXCHANGE; - - struct AuctionDetails { - uint256 beginTimeSeconds; // Auction begin unix timestamp: sellOrder.makerAssetData - uint256 endTimeSeconds; // Auction end unix timestamp: sellOrder.expiryTimeSeconds - uint256 beginAmount; // Auction begin amount: sellOrder.makerAssetData - uint256 endAmount; // Auction end amount: sellOrder.takerAssetAmount - uint256 currentAmount; // Calculated amount given block.timestamp - uint256 currentTimeSeconds; // block.timestamp - } - - constructor (address _exchange) - public - { - EXCHANGE = IExchange(_exchange); - } - - /// @dev Matches the buy and sell orders at an amount given the following: the current block time, the auction - /// start time and the auction begin amount. The sell order is a an order at the lowest amount - /// at the end of the auction. Excess from the match is transferred to the seller. - /// Over time the price moves from beginAmount to endAmount given the current block.timestamp. - /// sellOrder.expiryTimeSeconds is the end time of the auction. - /// sellOrder.takerAssetAmount is the end amount of the auction (lowest possible amount). - /// sellOrder.makerAssetData is the ABI encoded Asset Proxy data with the following data appended - /// buyOrder.makerAssetData is the buyers bid on the auction, must meet the amount for the current block timestamp - /// (uint256 beginTimeSeconds, uint256 beginAmount). - /// This function reverts in the following scenarios: - /// * Auction has not started (auctionDetails.currentTimeSeconds < auctionDetails.beginTimeSeconds) - /// * Auction has expired (auctionDetails.endTimeSeconds < auctionDetails.currentTimeSeconds) - /// * Amount is invalid: Buy order amount is too low (buyOrder.makerAssetAmount < auctionDetails.currentAmount) - /// * Amount is invalid: Invalid begin amount (auctionDetails.beginAmount > auctionDetails.endAmount) - /// * Any failure in the 0x Match Orders - /// @param buyOrder The Buyer's order. This order is for the current expected price of the auction. - /// @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction). - /// @param buySignature Proof that order was created by the buyer. - /// @param sellSignature Proof that order was created by the seller. - /// @return matchedFillResults amounts filled and fees paid by maker and taker of matched orders. - function matchOrders( - LibOrder.Order memory buyOrder, - LibOrder.Order memory sellOrder, - bytes memory buySignature, - bytes memory sellSignature - ) - public - returns (LibFillResults.MatchedFillResults memory matchedFillResults) - { - AuctionDetails memory auctionDetails = getAuctionDetails(sellOrder); - // Ensure the auction has not yet started - require( - auctionDetails.currentTimeSeconds >= auctionDetails.beginTimeSeconds, - "AUCTION_NOT_STARTED" - ); - // Ensure the auction has not expired. This will fail later in 0x but we can save gas by failing early - require( - sellOrder.expirationTimeSeconds > auctionDetails.currentTimeSeconds, - "AUCTION_EXPIRED" - ); - // Validate the buyer amount is greater than the current auction amount - require( - buyOrder.makerAssetAmount >= auctionDetails.currentAmount, - "INVALID_AMOUNT" - ); - // Match orders, maximally filling `buyOrder` - matchedFillResults = EXCHANGE.matchOrders( - buyOrder, - sellOrder, - buySignature, - sellSignature - ); - // The difference in sellOrder.takerAssetAmount and current amount is given as spread to the matcher - // This may include additional spread from the buyOrder.makerAssetAmount and the currentAmount. - // e.g currentAmount is 30, sellOrder.takerAssetAmount is 10 and buyOrder.makerAssetamount is 40. - // 10 (40-30) is returned to the buyer, 20 (30-10) sent to the seller and 10 has previously - // been transferred to the seller during matchOrders - uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; - if (leftMakerAssetSpreadAmount > 0) { - // ERC20 Asset data itself is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Header | 0 | 4 | function selector | - // | Params | | 1 * 32 | function parameters: | - // | | 4 | 12 | 1. token address padding | - // | | 16 | 20 | 2. token address | - bytes memory assetData = sellOrder.takerAssetData; - address token = assetData.readAddress(16); - // Calculate the excess from the buy order. This can occur if the buyer sends in a higher - // amount than the calculated current amount - uint256 buyerExcessAmount = buyOrder.makerAssetAmount.safeSub(auctionDetails.currentAmount); - uint256 sellerExcessAmount = leftMakerAssetSpreadAmount.safeSub(buyerExcessAmount); - // Return the difference between auctionDetails.currentAmount and sellOrder.takerAssetAmount - // to the seller - if (sellerExcessAmount > 0) { - IERC20Token(token).transfer(sellOrder.makerAddress, sellerExcessAmount); - } - // Return the difference between buyOrder.makerAssetAmount and auctionDetails.currentAmount - // to the buyer - if (buyerExcessAmount > 0) { - IERC20Token(token).transfer(buyOrder.makerAddress, buyerExcessAmount); - } - } - return matchedFillResults; - } - - /// @dev Calculates the Auction Details for the given order - /// @param order The sell order - /// @return AuctionDetails - function getAuctionDetails(LibOrder.Order memory order) - public - returns (AuctionDetails memory auctionDetails) - { - uint256 makerAssetDataLength = order.makerAssetData.length; - // It is unknown the encoded data of makerAssetData, we assume the last 64 bytes - // are the Auction Details encoding. - // Auction Details is encoded as follows: - // - // | Area | Offset | Length | Contents | - // |----------|--------|---------|-------------------------------------| - // | Params | | 2 * 32 | parameters: | - // | | -64 | 32 | 1. auction begin unix timestamp | - // | | -32 | 32 | 2. auction begin begin amount | - // ERC20 asset data length is 4+32, 64 for auction details results in min length 100 - require( - makerAssetDataLength >= 100, - "INVALID_ASSET_DATA" - ); - uint256 auctionBeginTimeSeconds = order.makerAssetData.readUint256(makerAssetDataLength - 64); - uint256 auctionBeginAmount = order.makerAssetData.readUint256(makerAssetDataLength - 32); - // Ensure the auction has a valid begin time - require( - order.expirationTimeSeconds > auctionBeginTimeSeconds, - "INVALID_BEGIN_TIME" - ); - uint256 auctionDurationSeconds = order.expirationTimeSeconds-auctionBeginTimeSeconds; - // Ensure the auction goes from high to low - uint256 minAmount = order.takerAssetAmount; - require( - auctionBeginAmount > minAmount, - "INVALID_AMOUNT" - ); - uint256 amountDelta = auctionBeginAmount-minAmount; - // solhint-disable-next-line not-rely-on-time - uint256 timestamp = block.timestamp; - auctionDetails.beginTimeSeconds = auctionBeginTimeSeconds; - auctionDetails.endTimeSeconds = order.expirationTimeSeconds; - auctionDetails.beginAmount = auctionBeginAmount; - auctionDetails.endAmount = minAmount; - auctionDetails.currentTimeSeconds = timestamp; - - uint256 remainingDurationSeconds = order.expirationTimeSeconds-timestamp; - if (timestamp < auctionBeginTimeSeconds) { - // If the auction has not yet begun the current amount is the auctionBeginAmount - auctionDetails.currentAmount = auctionBeginAmount; - } else if (timestamp >= order.expirationTimeSeconds) { - // If the auction has ended the current amount is the minAmount. - // Auction end time is guaranteed by 0x Exchange due to the order expiration - auctionDetails.currentAmount = minAmount; - } else { - auctionDetails.currentAmount = minAmount.safeAdd( - remainingDurationSeconds.safeMul(amountDelta).safeDiv(auctionDurationSeconds) - ); - } - return auctionDetails; - } -} diff --git a/contracts/exchange-libs/contracts/src/LibAssetDataTransfer.sol b/contracts/extensions/contracts/src/LibAssetDataTransfer.sol similarity index 99% rename from contracts/exchange-libs/contracts/src/LibAssetDataTransfer.sol rename to contracts/extensions/contracts/src/LibAssetDataTransfer.sol index b1121a5733..7f68b5bc7e 100644 --- a/contracts/exchange-libs/contracts/src/LibAssetDataTransfer.sol +++ b/contracts/extensions/contracts/src/LibAssetDataTransfer.sol @@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol"; import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; import "@0x/contracts-erc1155/contracts/src/interfaces/IERC1155.sol"; import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; -import "./LibAssetDataTransferRichErrors.sol"; +import "./rich-errors/LibAssetDataTransferRichErrors.sol"; library LibAssetDataTransfer { diff --git a/contracts/exchange-forwarder/contracts/src/MixinWeth.sol b/contracts/extensions/contracts/src/MixinWethUtils.sol similarity index 59% rename from contracts/exchange-forwarder/contracts/src/MixinWeth.sol rename to contracts/extensions/contracts/src/MixinWethUtils.sol index 0b92c900e2..66d1983ce6 100644 --- a/contracts/exchange-forwarder/contracts/src/MixinWeth.sol +++ b/contracts/extensions/contracts/src/MixinWethUtils.sol @@ -12,30 +12,57 @@ 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. + limitations \under the License. */ pragma solidity ^0.5.9; +import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol"; +import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol"; +import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; import "@0x/contracts-utils/contracts/src/LibRichErrors.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; -import "./libs/LibConstants.sol"; -import "./libs/LibForwarderRichErrors.sol"; +import "./rich-errors/LibWethUtilsRichErrors.sol"; -contract MixinWeth is - LibConstants -{ +contract MixinWethUtils { + + uint256 constant internal MAX_UINT256 = uint256(-1); + + // solhint-disable var-name-mixedcase + IEtherToken internal WETH; + // solhint-enable var-name-mixedcase + using LibSafeMath for uint256; + constructor ( + address exchange, + address weth + ) + public + { + WETH = IEtherToken(weth); + + address proxyAddress = IExchange(exchange).getAssetProxy(IAssetData(address(0)).ERC20Token.selector); + if (proxyAddress == address(0)) { + LibRichErrors.rrevert(LibWethUtilsRichErrors.UnregisteredAssetProxyError()); + } + WETH.approve(proxyAddress, MAX_UINT256); + + address protocolFeeCollector = IExchange(exchange).protocolFeeCollector(); + if (protocolFeeCollector != address(0)) { + WETH.approve(protocolFeeCollector, MAX_UINT256); + } + } + /// @dev Default payable function, this allows us to withdraw WETH function () external payable { - if (msg.sender != address(ETHER_TOKEN)) { - LibRichErrors.rrevert(LibForwarderRichErrors.DefaultFunctionWethContractOnlyError( + if (msg.sender != address(WETH)) { + LibRichErrors.rrevert(LibWethUtilsRichErrors.DefaultFunctionWethContractOnlyError( msg.sender )); } @@ -55,7 +82,7 @@ contract MixinWeth is uint256 feesLen = ethFeeAmounts.length; // ethFeeAmounts len must equal feeRecipients len if (feesLen != feeRecipients.length) { - LibRichErrors.rrevert(LibForwarderRichErrors.EthFeeLengthMismatchError( + LibRichErrors.rrevert(LibWethUtilsRichErrors.EthFeeLengthMismatchError( feesLen, feeRecipients.length )); @@ -69,7 +96,7 @@ contract MixinWeth is uint256 ethFeeAmount = ethFeeAmounts[i]; // Ensure there is enough ETH to pay the fee if (ethRemaining < ethFeeAmount) { - LibRichErrors.rrevert(LibForwarderRichErrors.InsufficientEthForFeeError( + LibRichErrors.rrevert(LibWethUtilsRichErrors.InsufficientEthForFeeError( ethFeeAmount, ethRemaining )); @@ -80,37 +107,24 @@ contract MixinWeth is } // Convert remaining ETH to WETH. - ETHER_TOKEN.deposit.value(ethRemaining)(); + WETH.deposit.value(ethRemaining)(); return ethRemaining; } - /// @dev Refunds any excess ETH to msg.sender. - /// @param initialWethAmount Amount of WETH available after transferring affiliate fees. - /// @param wethSpent Amount of WETH spent when filling orders. + /// @dev Unwraps and refunds WETH to msg.sender. + /// @param refundAmount Amount of WETH balance to refund. function _transferEthRefund( - uint256 initialWethAmount, - uint256 wethSpent + uint256 refundAmount ) internal { - // Ensure that no extra WETH owned by this contract has been spent. - if (wethSpent > initialWethAmount) { - LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError( - wethSpent, - msg.value - )); - } - - // Calculate amount of WETH that hasn't been spent. - uint256 wethRemaining = initialWethAmount.safeSub(wethSpent); - - // Do nothing if no WETH remaining - if (wethRemaining > 0) { - // Convert remaining WETH to ETH - ETHER_TOKEN.withdraw(wethRemaining); - // Transfer remaining ETH to sender - msg.sender.transfer(wethRemaining); + // Do nothing if no WETH to refund + if (refundAmount > 0) { + // Convert WETH to ETH + WETH.withdraw(refundAmount); + // Transfer ETH to sender + msg.sender.transfer(refundAmount); } } } diff --git a/contracts/extensions/contracts/src/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/src/OrderMatcher/MixinAssets.sol deleted file mode 100644 index ab811de11a..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/MixinAssets.sol +++ /dev/null @@ -1,195 +0,0 @@ -/* - - Copyright 2019 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.5.9; - -import "@0x/contracts-utils/contracts/src/LibBytes.sol"; -import "@0x/contracts-utils/contracts/src/Ownable.sol"; -import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol"; -import "@0x/contracts-erc721/contracts/src/interfaces/IERC721Token.sol"; -import "./libs/LibConstants.sol"; -import "./interfaces/IAssets.sol"; - - -contract MixinAssets is - IAssets, - Ownable, - LibConstants -{ - using LibBytes for bytes; - - /// @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 asset to withdraw. - function withdrawAsset( - bytes calldata assetData, - uint256 amount - ) - external - onlyOwner - { - _transferAssetToSender(assetData, amount); - } - - /// @dev Approves or disapproves an AssetProxy to spend asset. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param amount Amount of asset to approve for respective proxy. - function approveAssetProxy( - bytes calldata assetData, - uint256 amount - ) - external - onlyOwner - { - bytes4 proxyId = assetData.readBytes4(0); - - if (proxyId == ERC20_DATA_ID) { - _approveERC20Token(assetData, amount); - } else if (proxyId == ERC721_DATA_ID) { - _approveERC721Token(assetData, amount); - } else { - revert("UNSUPPORTED_ASSET_PROXY"); - } - } - - /// @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 - { - // 4 byte id + 12 0 bytes before ABI encoded token address. - 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. - // 4 byte id + 12 0 bytes before ABI encoded token address. - address token = assetData.readAddress(16); - // 4 byte id + 32 byte ABI encoded token address before token id. - uint256 tokenId = assetData.readUint256(36); - - // Perform transfer. - IERC721Token(token).transferFrom( - address(this), - msg.sender, - tokenId - ); - } - - /// @dev Sets approval for ERC20 AssetProxy. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param amount Amount of asset to approve for respective proxy. - function _approveERC20Token( - bytes memory assetData, - uint256 amount - ) - internal - { - address token = assetData.readAddress(16); - require( - IERC20Token(token).approve(ERC20_PROXY_ADDRESS, amount), - "APPROVAL_FAILED" - ); - } - - /// @dev Sets approval for ERC721 AssetProxy. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param amount Amount of asset to approve for respective proxy. - function _approveERC721Token( - bytes memory assetData, - uint256 amount - ) - internal - { - address token = assetData.readAddress(16); - bool approval = amount >= 1; - IERC721Token(token).setApprovalForAll(ERC721_PROXY_ADDRESS, approval); - } -} diff --git a/contracts/extensions/contracts/src/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/src/OrderMatcher/MixinMatchOrders.sol deleted file mode 100644 index 778fc87346..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/MixinMatchOrders.sol +++ /dev/null @@ -1,86 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "./libs/LibConstants.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; -import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol"; -import "@0x/contracts-utils/contracts/src/Ownable.sol"; - - -contract MixinMatchOrders is - Ownable, - LibConstants -{ - /// @dev Match two complementary orders that have a profitable spread. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the left order is then used to fill the right order as much as possible. - /// This results in a spread being taken in terms of both assets. The spread is held within this contract. - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftSignature Proof that order was created by the left maker. - /// @param rightSignature Proof that order was created by the right maker. - function matchOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - bytes memory leftSignature, - bytes memory rightSignature - ) - public - onlyOwner - { - // Match orders, maximally filling `leftOrder` - LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( - leftOrder, - rightOrder, - leftSignature, - rightSignature - ); - - uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; - uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount; - - // Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled - if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) { - return; - } - - // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. - rightOrder.makerAssetData = leftOrder.takerAssetData; - rightOrder.takerAssetData = leftOrder.makerAssetData; - - // Query `rightOrder` info to check if it has been completely filled - // We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call - LibOrder.OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder); - - // Do not attempt to call `fillOrder` if order has been completely filled - if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) { - return; - } - - // We do not need to pass in a signature since it was already validated in the `matchOrders` call - EXCHANGE.fillOrder( - rightOrder, - leftMakerAssetSpreadAmount, - "" - ); - } -} diff --git a/contracts/extensions/contracts/src/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/src/OrderMatcher/OrderMatcher.sol deleted file mode 100644 index 87133a76f8..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/OrderMatcher.sol +++ /dev/null @@ -1,38 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-utils/contracts/src/Ownable.sol"; -import "./libs/LibConstants.sol"; -import "./MixinMatchOrders.sol"; -import "./MixinAssets.sol"; - - -// solhint-disable no-empty-blocks -contract OrderMatcher is - MixinMatchOrders, - MixinAssets -{ - constructor (address _exchange) - public - LibConstants(_exchange) - Ownable() - {} -} diff --git a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IAssets.sol b/contracts/extensions/contracts/src/OrderMatcher/interfaces/IAssets.sol deleted file mode 100644 index 2d260673b9..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IAssets.sol +++ /dev/null @@ -1,43 +0,0 @@ -/* - - Copyright 2019 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.5.9; - - -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 asset to withdraw. - function withdrawAsset( - bytes calldata assetData, - uint256 amount - ) - external; - - /// @dev Approves or disapproves an AssetProxy to spend asset. - /// @param assetData Byte array encoded for the respective asset proxy. - /// @param amount Amount of asset to approve for respective proxy. - function approveAssetProxy( - bytes calldata assetData, - uint256 amount - ) - external; -} diff --git a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IMatchOrders.sol b/contracts/extensions/contracts/src/OrderMatcher/interfaces/IMatchOrders.sol deleted file mode 100644 index 9e48390cd1..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IMatchOrders.sol +++ /dev/null @@ -1,43 +0,0 @@ -/* - - Copyright 2019 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.5.9; -pragma experimental ABIEncoderV2; - -import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol"; - - -contract IMatchOrders { - - /// @dev Match two complementary orders that have a profitable spread. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the left order is then used to fill the right order as much as possible. - /// This results in a spread being taken in terms of both assets. The spread is held within this contract. - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftSignature Proof that order was created by the left maker. - /// @param rightSignature Proof that order was created by the right maker. - function matchOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - bytes memory leftSignature, - bytes memory rightSignature - ) - public; -} diff --git a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IOrderMatcher.sol b/contracts/extensions/contracts/src/OrderMatcher/interfaces/IOrderMatcher.sol deleted file mode 100644 index b904b86f9b..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/interfaces/IOrderMatcher.sol +++ /dev/null @@ -1,31 +0,0 @@ -/* - - Copyright 2019 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.5.9; - -import "@0x/contracts-utils/contracts/src/interfaces/IOwnable.sol"; -import "./IMatchOrders.sol"; -import "./IAssets.sol"; - - -// solhint-disable no-empty-blocks -contract IOrderMatcher is - IOwnable, - IMatchOrders, - IAssets -{} diff --git a/contracts/extensions/contracts/src/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/src/OrderMatcher/libs/LibConstants.sol deleted file mode 100644 index b51e1cb85c..0000000000 --- a/contracts/extensions/contracts/src/OrderMatcher/libs/LibConstants.sol +++ /dev/null @@ -1,56 +0,0 @@ -/* - - Copyright 2019 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.5.9; - -import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol"; - - -contract LibConstants { - - // bytes4(keccak256("transfer(address,uint256)")) - bytes4 constant internal ERC20_TRANSFER_SELECTOR = 0xa9059cbb; - // bytes4(keccak256("ERC20Token(address)")) - bytes4 constant internal ERC20_DATA_ID = 0xf47261b0; - // bytes4(keccak256("ERC721Token(address,uint256)")) - bytes4 constant internal ERC721_DATA_ID = 0x02571792; - - // solhint-disable var-name-mixedcase - IExchange internal EXCHANGE; - address internal ERC20_PROXY_ADDRESS; - address internal ERC721_PROXY_ADDRESS; - // solhint-enable var-name-mixedcase - - constructor (address _exchange) - public - { - EXCHANGE = IExchange(_exchange); - - ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_DATA_ID); - require( - ERC20_PROXY_ADDRESS != address(0), - "UNREGISTERED_ASSET_PROXY" - ); - - ERC721_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC721_DATA_ID); - require( - ERC721_PROXY_ADDRESS != address(0), - "UNREGISTERED_ASSET_PROXY" - ); - } -} diff --git a/contracts/exchange-libs/contracts/src/LibAssetDataTransferRichErrors.sol b/contracts/extensions/contracts/src/rich-errors/LibAssetDataTransferRichErrors.sol similarity index 100% rename from contracts/exchange-libs/contracts/src/LibAssetDataTransferRichErrors.sol rename to contracts/extensions/contracts/src/rich-errors/LibAssetDataTransferRichErrors.sol diff --git a/contracts/extensions/contracts/src/rich-errors/LibWethUtilsRichErrors.sol b/contracts/extensions/contracts/src/rich-errors/LibWethUtilsRichErrors.sol new file mode 100644 index 0000000000..c1b1e3369a --- /dev/null +++ b/contracts/extensions/contracts/src/rich-errors/LibWethUtilsRichErrors.sol @@ -0,0 +1,91 @@ +/* + + Copyright 2019 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.5.9; + + +library LibWethUtilsRichErrors { + + // bytes4(keccak256("UnregisteredAssetProxyError()")) + bytes4 internal constant UNREGISTERED_ASSET_PROXY_ERROR_SELECTOR = + 0xf3b96b8d; + + // bytes4(keccak256("InsufficientEthForFeeError(uint256,uint256)")) + bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR = + 0xecf40fd9; + + // bytes4(keccak256("DefaultFunctionWethContractOnlyError(address)")) + bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR = + 0x08b18698; + + // bytes4(keccak256("EthFeeLengthMismatchError(uint256,uint256)")) + bytes4 internal constant ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR = + 0x3ecb6ceb; + + // solhint-disable func-name-mixedcase + function UnregisteredAssetProxyError() + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector(UNREGISTERED_ASSET_PROXY_ERROR_SELECTOR); + } + + function InsufficientEthForFeeError( + uint256 ethFeeRequired, + uint256 ethAvailable + ) + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector( + INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR, + ethFeeRequired, + ethAvailable + ); + } + + function DefaultFunctionWethContractOnlyError( + address senderAddress + ) + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector( + DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR, + senderAddress + ); + } + + function EthFeeLengthMismatchError( + uint256 ethFeesLength, + uint256 feeRecipientsLength + ) + internal + pure + returns (bytes memory) + { + return abi.encodeWithSelector( + ETH_FEE_LENGTH_MISMATCH_ERROR_SELECTOR, + ethFeesLength, + feeRecipientsLength + ); + } +} diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index 7e4e3b7484..2cfec422a5 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -38,8 +38,7 @@ "docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { - "publicInterfaceContracts": "DutchAuction,OrderMatcher,BalanceThresholdFilter", - "abis": "./generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Exchange|ExchangeWrapper|OrderMatcher|WETH9).json", + "abis": "./test/generated-artifacts/@(LibAssetDataTransfer|LibAssetDataTransferRichErrors|LibWethUtilsRichErrors|MixinWethUtils).json", "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." }, "repository": { diff --git a/contracts/extensions/src/artifacts.ts b/contracts/extensions/src/artifacts.ts index 2a286d5e1d..74efc2f940 100644 --- a/contracts/extensions/src/artifacts.ts +++ b/contracts/extensions/src/artifacts.ts @@ -5,17 +5,13 @@ */ import { ContractArtifact } from 'ethereum-types'; -import * as BalanceThresholdFilter from '../generated-artifacts/BalanceThresholdFilter.json'; -import * as DutchAuction from '../generated-artifacts/DutchAuction.json'; -import * as Exchange from '../generated-artifacts/Exchange.json'; -import * as ExchangeWrapper from '../generated-artifacts/ExchangeWrapper.json'; -import * as OrderMatcher from '../generated-artifacts/OrderMatcher.json'; -import * as WETH9 from '../generated-artifacts/WETH9.json'; +import * as LibAssetDataTransfer from '../generated-artifacts/LibAssetDataTransfer.json'; +import * as LibAssetDataTransferRichErrors from '../generated-artifacts/LibAssetDataTransferRichErrors.json'; +import * as LibWethUtilsRichErrors from '../generated-artifacts/LibWethUtilsRichErrors.json'; +import * as MixinWethUtils from '../generated-artifacts/MixinWethUtils.json'; export const artifacts = { - WETH9: WETH9 as ContractArtifact, - ExchangeWrapper: ExchangeWrapper as ContractArtifact, - Exchange: Exchange as ContractArtifact, - BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact, - DutchAuction: DutchAuction as ContractArtifact, - OrderMatcher: OrderMatcher as ContractArtifact, + LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact, + MixinWethUtils: MixinWethUtils as ContractArtifact, + LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact, + LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact, }; diff --git a/contracts/extensions/src/index.ts b/contracts/extensions/src/index.ts index 2e5b9b2140..fee9973c79 100644 --- a/contracts/extensions/src/index.ts +++ b/contracts/extensions/src/index.ts @@ -1,5 +1,5 @@ export { artifacts } from './artifacts'; -export { BalanceThresholdFilterContract, DutchAuctionContract, OrderMatcherContract } from './wrappers'; +export { LibAssetDataTransferRevertErrors, MixinWethUtilsRevertErrors } from '@0x/utils'; export { ContractArtifact, ContractChains, diff --git a/contracts/extensions/src/wrappers.ts b/contracts/extensions/src/wrappers.ts index db081bf3e5..3784249d9b 100644 --- a/contracts/extensions/src/wrappers.ts +++ b/contracts/extensions/src/wrappers.ts @@ -3,6 +3,7 @@ * Warning: This file is auto-generated by contracts-gen. Don't edit manually. * ----------------------------------------------------------------------------- */ -export * from '../generated-wrappers/balance_threshold_filter'; -export * from '../generated-wrappers/dutch_auction'; -export * from '../generated-wrappers/order_matcher'; +export * from '../generated-wrappers/lib_asset_data_transfer'; +export * from '../generated-wrappers/lib_asset_data_transfer_rich_errors'; +export * from '../generated-wrappers/lib_weth_utils_rich_errors'; +export * from '../generated-wrappers/mixin_weth_utils'; diff --git a/contracts/extensions/test/artifacts.ts b/contracts/extensions/test/artifacts.ts new file mode 100644 index 0000000000..61561bf804 --- /dev/null +++ b/contracts/extensions/test/artifacts.ts @@ -0,0 +1,17 @@ +/* + * ----------------------------------------------------------------------------- + * Warning: This file is auto-generated by contracts-gen. Don't edit manually. + * ----------------------------------------------------------------------------- + */ +import { ContractArtifact } from 'ethereum-types'; + +import * as LibAssetDataTransfer from '../test/generated-artifacts/LibAssetDataTransfer.json'; +import * as LibAssetDataTransferRichErrors from '../test/generated-artifacts/LibAssetDataTransferRichErrors.json'; +import * as LibWethUtilsRichErrors from '../test/generated-artifacts/LibWethUtilsRichErrors.json'; +import * as MixinWethUtils from '../test/generated-artifacts/MixinWethUtils.json'; +export const artifacts = { + LibAssetDataTransfer: LibAssetDataTransfer as ContractArtifact, + MixinWethUtils: MixinWethUtils as ContractArtifact, + LibAssetDataTransferRichErrors: LibAssetDataTransferRichErrors as ContractArtifact, + LibWethUtilsRichErrors: LibWethUtilsRichErrors as ContractArtifact, +}; diff --git a/contracts/extensions/test/balance_threshold_filter.ts b/contracts/extensions/test/balance_threshold_filter.ts deleted file mode 100644 index 622ad5f5e9..0000000000 --- a/contracts/extensions/test/balance_threshold_filter.ts +++ /dev/null @@ -1,1589 +0,0 @@ -import { DevUtilsContract } from '@0x/contracts-dev-utils'; -import { ExchangeRevertErrors } from '@0x/contracts-exchange'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { Order, RevertReason, SignedOrder } from '@0x/types'; -import { BigNumber, providerUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy'; -import { DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { - chaiSetup, - constants, - ContractName, - ERC20BalancesByOwner, - OrderFactory, - OrderStatus, - provider, - TransactionFactory, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; - -import { BalanceThresholdWrapper } from './utils/balance_threshold_wrapper'; - -import { BalanceThresholdFilterContract } from './wrappers'; - -import { artifacts } from './artifacts'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const DECIMALS_DEFAULT = 18; - -interface ValidatedAddressesLog { - args: { addresses: string[] }; -} - -describe(ContractName.BalanceThresholdFilter, () => { - const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); - const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT); - const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT); - - let chainId: number; - let validMakerAddress: string; - let validMakerAddress2: string; - let owner: string; - let validTakerAddress: string; - let feeRecipientAddress: string; - let invalidAddress: string; - let defaultMakerAssetAddress: string; - let defaultTakerAssetAddress: string; - let zrxAssetData: string; - let zrxToken: DummyERC20TokenContract; - let exchangeInstance: ExchangeContract; - let devUtils: DevUtilsContract; - - let orderFactory: OrderFactory; - let orderFactory2: OrderFactory; - let invalidOrderFactory: OrderFactory; - let erc20Wrapper: ERC20Wrapper; - let erc20Balances: ERC20BalancesByOwner; - let erc20TakerBalanceThresholdWrapper: BalanceThresholdWrapper; - let erc721TakerBalanceThresholdWrapper: BalanceThresholdWrapper; - let erc721MakerBalanceThresholdWrapper: BalanceThresholdWrapper; - let erc721NonValidBalanceThresholdWrapper: BalanceThresholdWrapper; - - let defaultOrderParams: Partial; - let validSignedOrder: SignedOrder; - let validSignedOrder2: SignedOrder; - - let erc721BalanceThresholdFilterInstance: BalanceThresholdFilterContract; - let erc20BalanceThresholdFilterInstance: BalanceThresholdFilterContract; - - const assertValidatedAddressesLog = async ( - txReceipt: TransactionReceiptWithDecodedLogs, - expectedValidatedAddresses: string[], - ) => { - expect(txReceipt.logs.length).to.be.gte(1); - const validatedAddressesLog = (txReceipt.logs[0] as any) as ValidatedAddressesLog; - const validatedAddresses = validatedAddressesLog.args.addresses; - // @HACK-hysz: Nested addresses are not translated to lower-case but this will change once - // the new ABI Encoder/Decoder is used by the contract templates. - const validatedAddressesNormalized: string[] = []; - _.each(validatedAddresses, address => { - const normalizedAddress = _.toLower(address); - validatedAddressesNormalized.push(normalizedAddress); - }); - expect(validatedAddressesNormalized).to.be.deep.equal(expectedValidatedAddresses); - }; - - before(async () => { - // Get the chain ID. - chainId = await providerUtils.getChainIdAsync(provider); - // Create accounts - await blockchainLifecycle.startAsync(); - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([ - owner, - validMakerAddress, - validMakerAddress2, - validTakerAddress, - feeRecipientAddress, - invalidAddress, - ] = accounts); - const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validTakerAddress)]; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress)]; - const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress2)]; - const invalidAddressPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(invalidAddress)]; - devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); - // Create wrappers - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - const validAddresses = _.cloneDeepWith(usedAddresses); - _.remove(validAddresses, (address: string) => { - return address === invalidAddress; - }); - const erc721Wrapper = new ERC721Wrapper(provider, validAddresses, owner); - // Deploy ERC20 tokens - const numDummyErc20ToDeploy = 4; - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let erc20BalanceThresholdAsset: DummyERC20TokenContract; - [erc20TokenA, erc20TokenB, zrxToken, erc20BalanceThresholdAsset] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - defaultMakerAssetAddress = erc20TokenA.address; - defaultTakerAssetAddress = erc20TokenB.address; - zrxAssetData = await devUtils.encodeERC20AssetData(zrxToken.address).callAsync(); - // Create proxies - const erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - // Deploy Exchange contract - exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - artifacts, - zrxAssetData, - new BigNumber(chainId), - ); - // Register proxies - await exchangeInstance.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({ - from: owner, - }); - await erc20Proxy.addAuthorizedAddress(exchangeInstance.address).awaitTransactionSuccessAsync({ - from: owner, - }); - // Deploy Balance Threshold Filters - // One uses an ERC721 token as its balance threshold asset; the other uses an ERC20 - const erc721alanceThreshold = new BigNumber(1); - await erc721Wrapper.deployProxyAsync(); - const [erc721BalanceThresholdAsset] = await erc721Wrapper.deployDummyTokensAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - erc721BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( - artifacts.BalanceThresholdFilter, - provider, - txDefaults, - artifacts, - exchangeInstance.address, - erc721BalanceThresholdAsset.address, - erc721alanceThreshold, - ); - const erc20BalanceThreshold = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 10); - erc20BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( - artifacts.BalanceThresholdFilter, - provider, - txDefaults, - artifacts, - exchangeInstance.address, - erc20BalanceThresholdAsset.address, - erc20BalanceThreshold, - ); - // Default order parameters - defaultOrderParams = { - feeRecipientAddress, - makerAssetData: await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(), - takerAssetData: await devUtils.encodeERC20AssetData(defaultTakerAssetAddress).callAsync(), - makerAssetAmount, - takerAssetAmount, - makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), DECIMALS_DEFAULT), - takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(150), DECIMALS_DEFAULT), - senderAddress: erc721BalanceThresholdFilterInstance.address, - exchangeAddress: exchangeInstance.address, - chainId, - }; - // Create two order factories with valid makers (who meet the threshold balance), and - // one factory for an invalid address (that does not meet the threshold balance) - // Valid order factory #1 - const defaultOrderParams1 = { - makerAddress: validMakerAddress, - ...defaultOrderParams, - }; - orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams1); - // Valid order factory #2 - const defaultOrderParams2 = { - makerAddress: validMakerAddress2, - ...defaultOrderParams, - }; - orderFactory2 = new OrderFactory(secondMakerPrivateKey, defaultOrderParams2); - // Invalid order factory - const defaultNonValidOrderParams = { - makerAddress: invalidAddress, - ...defaultOrderParams, - }; - invalidOrderFactory = new OrderFactory(invalidAddressPrivateKey, defaultNonValidOrderParams); - // Create Balance Thresold Wrappers - erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc20BalanceThresholdFilterInstance, - exchangeInstance, - new TransactionFactory(takerPrivateKey, exchangeInstance.address, chainId), - provider, - ); - erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721BalanceThresholdFilterInstance, - exchangeInstance, - new TransactionFactory(takerPrivateKey, exchangeInstance.address, chainId), - provider, - ); - erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721BalanceThresholdFilterInstance, - exchangeInstance, - new TransactionFactory(makerPrivateKey, exchangeInstance.address, chainId), - provider, - ); - erc721NonValidBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721BalanceThresholdFilterInstance, - exchangeInstance, - new TransactionFactory(invalidAddressPrivateKey, exchangeInstance.address, chainId), - provider, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - - describe('General Sanity Checks', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both maker/taker when both maker and taker exceed the balance threshold of an ERC20 token', async () => { - const validSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({ - ...defaultOrderParams, - makerAddress: validMakerAddress, - senderAddress: erc20TakerBalanceThresholdWrapper.getBalanceThresholdAddress(), - }); - // Execute a valid fill - const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync( - validSignedOrderERC20Sender, - validTakerAddress, - { takerAssetFillAmount }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)), - ); - }); - it('should revert if the Exchange transaction function is not supported', async () => { - // Create signed order without the fillOrder function selector - const salt = new BigNumber(0); - const badSelectorHex = '0x00000000'; - const signatureHex = '0x'; - // Call valid forwarder - const tx = erc721BalanceThresholdFilterInstance - .executeTransaction(salt, validTakerAddress, badSelectorHex, signatureHex) - .sendTransactionAsync(); - return expect(tx).to.revertWith(RevertReason.InvalidOrBlockedExchangeSelector); - }); - it('should revert if senderAddress is not set to the valid forwarding contract', async () => { - // Create signed order with incorrect senderAddress - const notBalanceThresholdFilterAddress = zrxToken.address; - const signedOrderWithBadSenderAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: notBalanceThresholdFilterAddress, - }); - const expectedError = new ExchangeRevertErrors.TransactionExecutionError(); - // Call valid forwarder - const tx = erc721TakerBalanceThresholdWrapper.fillOrderAsync( - signedOrderWithBadSenderAddress, - validTakerAddress, - { takerAssetFillAmount }, - ); - return expect(tx).to.revertWith(expectedError); - }); - }); - - describe('batchFillOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, { - takerAssetFillAmounts, - }); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount) - .times(2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const tx = erc721NonValidBalanceThresholdWrapper.batchFillOrdersAsync(orders, invalidAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('batchFillOrdersNoThrow', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( - orders, - validTakerAddress, - { - takerAssetFillAmounts, - }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount) - .times(2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, validTakerAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const tx = erc721NonValidBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, invalidAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('batchFillOrKillOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( - orders, - validTakerAddress, - { takerAssetFillAmounts }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount) - .times(2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount.times(2)), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.times(2).plus(takerFeePaid)), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const tx = erc721NonValidBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, invalidAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if one takerAssetFillAmount is not fully filled', async () => { - const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2); - const orders = [validSignedOrder, validSignedOrder2]; - const takerAssetFillAmounts = [takerAssetFillAmount, tooBigTakerAssetFillAmount]; - const expectedError = new ExchangeRevertErrors.TransactionExecutionError(); - // Call valid forwarder - const tx = erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, { - takerAssetFillAmounts, - }); - return expect(tx).to.revertWith(expectedError); - }); - }); - - describe('fillOrder', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { - // Execute a valid fill - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync( - validSignedOrder, - validTakerAddress, - { takerAssetFillAmount }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)), - ); - }); - it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721BalanceThresholdFilterInstance.address, - makerAddress: invalidAddress, - }); - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.fillOrderAsync( - signedOrderWithBadMakerAddress, - validTakerAddress, - { takerAssetFillAmount }, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const tx = erc721NonValidBalanceThresholdWrapper.fillOrderAsync(validSignedOrder, invalidAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('fillOrderNoThrow', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { - // Execute a valid fill - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( - validSignedOrder, - validTakerAddress, - { - takerAssetFillAmount, - }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)), - ); - }); - it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721BalanceThresholdFilterInstance.address, - makerAddress: invalidAddress, - }); - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( - signedOrderWithBadMakerAddress, - validTakerAddress, - { takerAssetFillAmount }, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const tx = erc721NonValidBalanceThresholdWrapper.fillOrderNoThrowAsync(validSignedOrder, invalidAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('fillOrKillOrder', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { - // Execute a valid fill - const takerAssetFillAmount_ = validSignedOrder.takerAssetAmount; - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( - validSignedOrder, - validTakerAddress, - { takerAssetFillAmount: takerAssetFillAmount_ }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount = takerAssetFillAmount_ - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid = validSignedOrder.makerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee - .times(makerAssetFillAmount) - .dividedToIntegerBy(validSignedOrder.makerAssetAmount); - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount_), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), - ); - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount_), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(makerFeePaid.plus(takerFeePaid)), - ); - }); - it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721BalanceThresholdFilterInstance.address, - makerAddress: invalidAddress, - }); - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( - signedOrderWithBadMakerAddress, - validTakerAddress, - { takerAssetFillAmount }, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const tx = erc721NonValidBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, invalidAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if takerAssetFillAmount is not fully filled', async () => { - const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2); - const expectedError = new ExchangeRevertErrors.TransactionExecutionError(); - const tx = erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, validTakerAddress, { - takerAssetFillAmount: tooBigTakerAssetFillAmount, - }); - return expect(tx).to.revertWith(expectedError); - }); - }); - - describe('marketSellOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync( - orders, - validTakerAddress, - { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount2 = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid2 = validSignedOrder2.makerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid2 = validSignedOrder2.takerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); - const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address] - .plus(validSignedOrder.makerFee) - .plus(makerFeePaid2) - .plus(takerFeePaid), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, validTakerAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const tx = erc721NonValidBalanceThresholdWrapper.marketSellOrdersAsync(orders, invalidAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('marketSellOrdersNoThrow', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( - orders, - validTakerAddress, - { - takerAssetFillAmount: cumulativeTakerAssetFillAmount, - }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerAssetFillAmount2 = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const makerFeePaid2 = validSignedOrder2.makerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid2 = validSignedOrder2.takerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); - const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address] - .plus(validSignedOrder.makerFee) - .plus(makerFeePaid2) - .plus(takerFeePaid), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, validTakerAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const tx = erc721NonValidBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, invalidAddress, { - takerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('marketBuyOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const makerAssetFillAmount2 = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, { - makerAssetFillAmount: cumulativeMakerAssetFillAmount, - }); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerFeePaid2 = validSignedOrder2.makerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid2 = validSignedOrder2.takerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address] - .plus(validSignedOrder.makerFee) - .plus(makerFeePaid2) - .plus(takerFeePaid), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const dummyMakerAssetFillAmount = new BigNumber(0); - const tx = erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, { - makerAssetFillAmount: dummyMakerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const dummyMakerAssetFillAmount = new BigNumber(0); - const tx = erc721NonValidBalanceThresholdWrapper.marketBuyOrdersAsync(orders, invalidAddress, { - makerAssetFillAmount: dummyMakerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('marketBuyOrdersNoThrowAsync', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { - // Execute a valid fill - const orders = [validSignedOrder, validSignedOrder2]; - const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const makerAssetFillAmount2 = takerAssetFillAmount - .times(validSignedOrder.makerAssetAmount) - .dividedToIntegerBy(validSignedOrder.takerAssetAmount); - const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( - orders, - validTakerAddress, - { - makerAssetFillAmount: cumulativeMakerAssetFillAmount, - // HACK(albrow): We need to hardcode the gas estimate here because - // the Geth gas estimator doesn't work with the way we use - // delegatecall and swallow errors. - gas: 600000, - }, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validSignedOrder2.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerFeePaid2 = validSignedOrder2.makerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid2 = validSignedOrder2.takerFee - .times(makerAssetFillAmount2) - .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); - const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); - // Maker #1 - expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), - ); - expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].plus(validSignedOrder.takerAssetAmount), - ); - expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), - ); - // Maker #2 - expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), - ); - expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][defaultTakerAssetAddress].plus(takerAssetFillAmount), - ); - expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), - ); - // Taker - expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus(cumulativeMakerAssetFillAmount), - ); - expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), - ); - // Fee recipient - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address] - .plus(validSignedOrder.makerFee) - .plus(makerFeePaid2) - .plus(takerFeePaid), - ); - }); - it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: invalidAddress, - }); - const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; - // Execute transaction - const dummyMakerAssetFillAmount = new BigNumber(0); - const tx = erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, validTakerAddress, { - makerAssetFillAmount: dummyMakerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const orders = [validSignedOrder, validSignedOrder2]; - const dummyMakerAssetFillAmount = new BigNumber(0); - const tx = erc721NonValidBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, invalidAddress, { - makerAssetFillAmount: dummyMakerAssetFillAmount, - }); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('matchOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('Should transfer correct amounts when both makers and taker meet the balance threshold', async () => { - // Test values/results taken from Match Orders test: - // 'Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil' - // Create orders to match - const signedOrderLeft = await orderFactory.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(17), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0), - makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - feeRecipientAddress, - }); - const signedOrderRight = await orderFactory2.newSignedOrderAsync({ - makerAssetData: await devUtils.encodeERC20AssetData(defaultTakerAssetAddress).callAsync(), - takerAssetData: await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - feeRecipientAddress, - }); - // Compute expected transfer amounts - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47% - // Right Maker - amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), - amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), - feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - // Taker - amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 0), - feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53% - feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% - }; - const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync( - signedOrderLeft, - signedOrderRight, - validTakerAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses = [ - signedOrderLeft.makerAddress, - signedOrderRight.makerAddress, - validTakerAddress, - ]; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check balances - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect( - newBalances[signedOrderLeft.makerAddress][defaultMakerAssetAddress], - 'Checking left maker egress ERC20 account balance', - ).to.be.bignumber.equal( - erc20Balances[signedOrderLeft.makerAddress][defaultMakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect( - newBalances[signedOrderRight.makerAddress][defaultTakerAssetAddress], - 'Checking right maker ingress ERC20 account balance', - ).to.be.bignumber.equal( - erc20Balances[signedOrderRight.makerAddress][defaultTakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect( - newBalances[validTakerAddress][defaultMakerAssetAddress], - 'Checking taker ingress ERC20 account balance', - ).to.be.bignumber.equal( - erc20Balances[validTakerAddress][defaultMakerAssetAddress].plus( - expectedTransferAmounts.amountReceivedByTaker, - ), - ); - expect( - newBalances[signedOrderLeft.makerAddress][defaultTakerAssetAddress], - 'Checking left maker ingress ERC20 account balance', - ).to.be.bignumber.equal( - erc20Balances[signedOrderLeft.makerAddress][defaultTakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect( - newBalances[signedOrderRight.makerAddress][defaultMakerAssetAddress], - 'Checking right maker egress ERC20 account balance', - ).to.be.bignumber.equal( - erc20Balances[signedOrderRight.makerAddress][defaultMakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - // Paid fees - expect( - newBalances[signedOrderLeft.makerAddress][zrxToken.address], - 'Checking left maker egress ERC20 account fees', - ).to.be.bignumber.equal( - erc20Balances[signedOrderLeft.makerAddress][zrxToken.address].minus( - expectedTransferAmounts.feePaidByLeftMaker, - ), - ); - expect( - newBalances[signedOrderRight.makerAddress][zrxToken.address], - 'Checking right maker egress ERC20 account fees', - ).to.be.bignumber.equal( - erc20Balances[signedOrderRight.makerAddress][zrxToken.address].minus( - expectedTransferAmounts.feePaidByRightMaker, - ), - ); - expect( - newBalances[validTakerAddress][zrxToken.address], - 'Checking taker egress ERC20 account fees', - ).to.be.bignumber.equal( - erc20Balances[validTakerAddress][zrxToken.address] - .minus(expectedTransferAmounts.feePaidByTakerLeft) - .minus(expectedTransferAmounts.feePaidByTakerRight), - ); - // Received fees - expect( - newBalances[signedOrderLeft.feeRecipientAddress][zrxToken.address], - 'Checking left fee recipient ingress ERC20 account fees', - ).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address] - .plus(expectedTransferAmounts.feePaidByLeftMaker) - .plus(expectedTransferAmounts.feePaidByRightMaker) - .plus(expectedTransferAmounts.feePaidByTakerLeft) - .plus(expectedTransferAmounts.feePaidByTakerRight), - ); - }); - it('should revert if left maker does not meet the balance threshold', async () => { - // Create signed order with non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721BalanceThresholdFilterInstance.address, - makerAddress: invalidAddress, - }); - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.matchOrdersAsync( - validSignedOrder, - signedOrderWithBadMakerAddress, - validTakerAddress, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if right maker does not meet the balance threshold', async () => { - // Create signed order with non-valid maker address - const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721BalanceThresholdFilterInstance.address, - makerAddress: invalidAddress, - }); - // Execute transaction - const tx = erc721TakerBalanceThresholdWrapper.matchOrdersAsync( - signedOrderWithBadMakerAddress, - validSignedOrder, - validTakerAddress, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - it('should revert if taker does not meet the balance threshold', async () => { - const tx = erc721NonValidBalanceThresholdWrapper.matchOrdersAsync( - validSignedOrder, - validSignedOrder, - invalidAddress, - ); - return expect(tx).to.revertWith(RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold); - }); - }); - - describe('cancelOrder', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - validSignedOrder = await orderFactory.newSignedOrderAsync(); - validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); - }); - it('Should successfully cancel order if maker meets balance threshold', async () => { - // Verify order is not cancelled - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - validSignedOrder, - ); - expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - // Cancel - const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync( - validSignedOrder, - validSignedOrder.makerAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - validSignedOrder, - ); - expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - }); - it('Should successfully cancel order if maker does not meet balance threshold', async () => { - // Create order where maker does not meet balance threshold - const signedOrderWithBadMakerAddress = await invalidOrderFactory.newSignedOrderAsync({}); - // Verify order is not cancelled - const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( - signedOrderWithBadMakerAddress, - ); - expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - // Cancel - const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrderAsync( - signedOrderWithBadMakerAddress, - signedOrderWithBadMakerAddress.makerAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - signedOrderWithBadMakerAddress, - ); - expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - }); - }); - - describe('batchCancelOrders', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - }); - it('Should successfully batch cancel orders if maker meets balance threshold', async () => { - // Create orders to cancel - const validSignedOrders = [ - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - await orderFactory.newSignedOrderAsync(), - ]; - // Verify orders are not cancelled - _.each(validSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - }); - // Cancel - const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync( - validSignedOrders, - validSignedOrders[0].makerAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - _.each(validSignedOrders, async signedOrder => { - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - }); - }); - it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { - // Create orders to cancel - const invalidSignedOrders = [ - await invalidOrderFactory.newSignedOrderAsync(), - await invalidOrderFactory.newSignedOrderAsync(), - await invalidOrderFactory.newSignedOrderAsync(), - ]; - // Verify orders are not cancelled - _.each(invalidSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - }); - // Cancel - const txReceipt = await erc721NonValidBalanceThresholdWrapper.batchCancelOrdersAsync( - invalidSignedOrders, - invalidAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - _.each(invalidSignedOrders, async signedOrder => { - const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - }); - }); - }); - - describe('cancelOrdersUpTo', () => { - beforeEach(async () => { - erc20Balances = await erc20Wrapper.getBalancesAsync(); - }); - it('Should successfully batch cancel orders if maker meets balance threshold', async () => { - // Create orders to cancel - const validSignedOrders = [ - await orderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), - await orderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), - await orderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), - ]; - // Verify orders are not cancelled - _.each(validSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - }); - // Cancel - const cancelOrdersUpToThisSalt = new BigNumber(1); - const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync( - cancelOrdersUpToThisSalt, - validSignedOrders[0].makerAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - _.each(validSignedOrders, async (signedOrder, salt: number) => { - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - const saltAsBigNumber = new BigNumber(salt); - if (saltAsBigNumber.isLessThanOrEqualTo(cancelOrdersUpToThisSalt)) { - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - } else { - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - } - }); - }); - it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { - // Create orders to cancel - const invalidSignedOrders = [ - await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), - await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), - await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), - ]; - // Verify orders are not cancelled - _.each(invalidSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - }); - // Cancel - const cancelOrdersUpToThisSalt = new BigNumber(1); - const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrdersUpToAsync( - cancelOrdersUpToThisSalt, - invalidAddress, - ); - // Assert validated addresses - const expectedValidatedAddresseses: string[] = []; - await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); - // Check that order was cancelled - _.each(invalidSignedOrders, async (signedOrder, salt: number) => { - const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( - signedOrder, - ); - const saltAsBigNumber = new BigNumber(salt); - if (saltAsBigNumber.isLessThanOrEqualTo(cancelOrdersUpToThisSalt)) { - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Cancelled); - } else { - return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.Fillable); - } - }); - }); - }); -}); -// tslint:disable:max-file-line-count -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/test/dutch_auction.ts b/contracts/extensions/test/dutch_auction.ts deleted file mode 100644 index f38c1ebdd9..0000000000 --- a/contracts/extensions/test/dutch_auction.ts +++ /dev/null @@ -1,368 +0,0 @@ -import { ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy'; -import { DevUtilsContract } from '@0x/contracts-dev-utils'; -import { DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { DummyERC721TokenContract } from '@0x/contracts-erc721'; -import { ExchangeContract } from '@0x/contracts-exchange'; -import { - chaiSetup, - constants, - ContractName, - ERC20BalancesByOwner, - getLatestBlockTimestampAsync, - OrderFactory, - provider, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { generatePseudoRandomSalt } from '@0x/order-utils'; -import { RevertReason, SignedOrder } from '@0x/types'; -import { BigNumber, providerUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import * as _ from 'lodash'; - -import { DutchAuctionContract, DutchAuctionTestWrapper, WETH9Contract } from './wrappers'; - -import { artifacts } from './artifacts'; - -import { encodeDutchAuctionAssetData } from './utils'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const DECIMALS_DEFAULT = 18; - -describe(ContractName.DutchAuction, () => { - let chainId: number; - let makerAddress: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddress: string; - let defaultMakerAssetAddress: string; - - let zrxToken: DummyERC20TokenContract; - let erc20TokenA: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let dutchAuctionContract: DutchAuctionContract; - let wethContract: WETH9Contract; - - let sellerOrderFactory: OrderFactory; - let buyerOrderFactory: OrderFactory; - let erc20Wrapper: ERC20Wrapper; - let erc20Balances: ERC20BalancesByOwner; - let currentBlockTimestamp: number; - let auctionBeginTimeSeconds: BigNumber; - let auctionEndTimeSeconds: BigNumber; - let auctionBeginAmount: BigNumber; - let auctionEndAmount: BigNumber; - let sellOrder: SignedOrder; - let buyOrder: SignedOrder; - let erc721MakerAssetIds: BigNumber[]; - const tenMinutesInSeconds = 10 * 60; - - let dutchAuctionTestWrapper: DutchAuctionTestWrapper; - let defaultERC20MakerAssetData: string; - - const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider); - - before(async () => { - await blockchainLifecycle.startAsync(); - - chainId = await providerUtils.getChainIdAsync(provider); - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts); - - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - - const numDummyErc20ToDeploy = 2; - [erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - const erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - const erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - - wethContract = await WETH9Contract.deployFrom0xArtifactAsync(artifacts.WETH9, provider, txDefaults, artifacts); - erc20Wrapper.addDummyTokenContract(wethContract as any); - - const zrxAssetData = await devUtils.encodeERC20AssetData(zrxToken.address).callAsync(); - const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - artifacts, - zrxAssetData, - new BigNumber(chainId), - ); - await exchangeInstance.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { - from: owner, - }); - await exchangeInstance.registerAssetProxy.awaitTransactionSuccessAsync(erc721Proxy.address, { - from: owner, - }); - - await erc20Proxy.addAuthorizedAddress(exchangeInstance.address).sendTransactionAsync({ - from: owner, - }); - await erc721Proxy.addAuthorizedAddress(exchangeInstance.address).sendTransactionAsync({ - from: owner, - }); - - const dutchAuctionInstance = await DutchAuctionContract.deployFrom0xArtifactAsync( - artifacts.DutchAuction, - provider, - txDefaults, - artifacts, - exchangeInstance.address, - ); - dutchAuctionContract = new DutchAuctionContract(dutchAuctionInstance.address, provider); - dutchAuctionTestWrapper = new DutchAuctionTestWrapper(dutchAuctionInstance, provider); - - defaultMakerAssetAddress = erc20TokenA.address; - const defaultTakerAssetAddress = wethContract.address; - - // Set up taker WETH balance and allowance - await web3Wrapper.awaitTransactionSuccessAsync( - await wethContract.deposit.sendTransactionAsync({ - from: takerAddress, - value: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT), - }), - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await wethContract - .approve(erc20Proxy.address, constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) - .sendTransactionAsync({ from: takerAddress }), - ); - web3Wrapper.abiDecoder.addABI(exchangeInstance.abi); - web3Wrapper.abiDecoder.addABI(zrxToken.abi); - erc20Wrapper.addTokenOwnerAddress(dutchAuctionContract.address); - - currentBlockTimestamp = await getLatestBlockTimestampAsync(); - // Default auction begins 10 minutes ago - auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp).minus(tenMinutesInSeconds); - // Default auction ends 10 from now - auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds); - auctionBeginAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT); - auctionEndAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT); - - // Default sell order and buy order are exact mirrors - const sellerDefaultOrderParams = { - salt: generatePseudoRandomSalt(), - makerAddress, - feeRecipientAddress, - // taker address or sender address should be set to the ducth auction contract - takerAddress: dutchAuctionContract.address, - makerAssetData: encodeDutchAuctionAssetData( - await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(), - auctionBeginTimeSeconds, - auctionBeginAmount, - ), - takerAssetData: await devUtils.encodeERC20AssetData(defaultTakerAssetAddress).callAsync(), - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT), - takerAssetAmount: auctionEndAmount, - expirationTimeSeconds: auctionEndTimeSeconds, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - exchangeAddress: exchangeInstance.address, - chainId, - }; - // Default buy order is for the auction begin price - const buyerDefaultOrderParams = { - ...sellerDefaultOrderParams, - makerAddress: takerAddress, - makerAssetData: sellerDefaultOrderParams.takerAssetData, - takerAssetData: sellerDefaultOrderParams.makerAssetData, - makerAssetAmount: auctionBeginAmount, - takerAssetAmount: sellerDefaultOrderParams.makerAssetAmount, - }; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)]; - const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)]; - sellerOrderFactory = new OrderFactory(makerPrivateKey, sellerDefaultOrderParams); - buyerOrderFactory = new OrderFactory(takerPrivateKey, buyerDefaultOrderParams); - defaultERC20MakerAssetData = await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - erc20Balances = await erc20Wrapper.getBalancesAsync(); - sellOrder = await sellerOrderFactory.newSignedOrderAsync(); - buyOrder = await buyerOrderFactory.newSignedOrderAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('matchOrders', () => { - it('should be worth the begin price at the begining of the auction', async () => { - auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp + 2); - const makerAssetData = encodeDutchAuctionAssetData( - defaultERC20MakerAssetData, - auctionBeginTimeSeconds, - auctionBeginAmount, - ); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ makerAssetData }); - const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - expect(auctionDetails.currentTimeSeconds).to.be.bignumber.lte(auctionBeginTimeSeconds); - expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionBeginAmount); - expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); - }); - it('should be be worth the end price at the end of the auction', async () => { - auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); - auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); - const makerAssetData = encodeDutchAuctionAssetData( - defaultERC20MakerAssetData, - auctionBeginTimeSeconds, - auctionBeginAmount, - ); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerAssetData, - expirationTimeSeconds: auctionEndTimeSeconds, - }); - const auctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - expect(auctionDetails.currentTimeSeconds).to.be.bignumber.gte(auctionEndTimeSeconds); - expect(auctionDetails.currentAmount).to.be.bignumber.equal(auctionEndAmount); - expect(auctionDetails.beginAmount).to.be.bignumber.equal(auctionBeginAmount); - }); - it('should match orders at current amount and send excess to buyer', async () => { - const beforeAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - buyOrder = await buyerOrderFactory.newSignedOrderAsync({ - makerAssetAmount: beforeAuctionDetails.currentAmount.times(2), - }); - await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[dutchAuctionContract.address][wethContract.address]).to.be.bignumber.equal( - constants.ZERO_AMOUNT, - ); - // HACK gte used here due to a bug in ganache where the timestamp can change - // between multiple calls to the same block. Which can move the amount in our case - // ref: https://github.com/trufflesuite/ganache-core/issues/111 - expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( - erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), - ); - expect(newBalances[takerAddress][wethContract.address]).to.be.bignumber.gte( - erc20Balances[takerAddress][wethContract.address].minus(beforeAuctionDetails.currentAmount), - ); - }); - it('maker fees on sellOrder are paid to the fee receipient', async () => { - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerFee: new BigNumber(1), - }); - await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( - erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(sellOrder.makerFee), - ); - }); - it('maker fees on buyOrder are paid to the fee receipient', async () => { - buyOrder = await buyerOrderFactory.newSignedOrderAsync({ - makerFee: new BigNumber(1), - }); - await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - const newBalances = await erc20Wrapper.getBalancesAsync(); - const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( - erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), - ); - expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].plus(buyOrder.makerFee), - ); - }); - it('should revert when auction expires', async () => { - auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds * 2); - auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds); - const makerAssetData = encodeDutchAuctionAssetData( - defaultERC20MakerAssetData, - auctionBeginTimeSeconds, - auctionBeginAmount, - ); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - expirationTimeSeconds: auctionEndTimeSeconds, - makerAssetData, - }); - const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - return expect(tx).to.revertWith(RevertReason.AuctionExpired); - }); - it('cannot be filled for less than the current price', async () => { - buyOrder = await buyerOrderFactory.newSignedOrderAsync({ - makerAssetAmount: sellOrder.takerAssetAmount, - }); - const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - return expect(tx).to.revertWith(RevertReason.AuctionInvalidAmount); - }); - it('auction begin amount must be higher than final amount ', async () => { - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - takerAssetAmount: auctionBeginAmount.plus(1), - }); - const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - return expect(tx).to.revertWith(RevertReason.AuctionInvalidAmount); - }); - it('begin time is less than end time', async () => { - auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds); - const makerAssetData = encodeDutchAuctionAssetData( - defaultERC20MakerAssetData, - auctionBeginTimeSeconds, - auctionBeginAmount, - ); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - expirationTimeSeconds: auctionEndTimeSeconds, - makerAssetData, - }); - const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - return expect(tx).to.revertWith(RevertReason.AuctionInvalidBeginTime); - }); - it('asset data contains auction parameters', async () => { - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerAssetData: await devUtils.encodeERC20AssetData(defaultMakerAssetAddress).callAsync(), - }); - const tx = dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - return expect(tx).to.revertWith(RevertReason.InvalidAssetData); - }); - - describe('ERC721', () => { - it('should match orders when ERC721', async () => { - const makerAssetId = erc721MakerAssetIds[0]; - const erc721MakerAssetData = await devUtils - .encodeERC721AssetData(erc721Token.address, makerAssetId) - .callAsync(); - const makerAssetData = encodeDutchAuctionAssetData( - erc721MakerAssetData, - auctionBeginTimeSeconds, - auctionBeginAmount, - ); - sellOrder = await sellerOrderFactory.newSignedOrderAsync({ - makerAssetAmount: new BigNumber(1), - makerAssetData, - }); - buyOrder = await buyerOrderFactory.newSignedOrderAsync({ - takerAssetAmount: new BigNumber(1), - takerAssetData: sellOrder.makerAssetData, - }); - await dutchAuctionTestWrapper.matchOrdersAsync(buyOrder, sellOrder, takerAddress); - const afterAuctionDetails = await dutchAuctionTestWrapper.getAuctionDetailsAsync(sellOrder); - const newBalances = await erc20Wrapper.getBalancesAsync(); - // HACK gte used here due to a bug in ganache where the timestamp can change - // between multiple calls to the same block. Which can move the amount in our case - // ref: https://github.com/trufflesuite/ganache-core/issues/111 - expect(newBalances[makerAddress][wethContract.address]).to.be.bignumber.gte( - erc20Balances[makerAddress][wethContract.address].plus(afterAuctionDetails.currentAmount), - ); - const newOwner = await erc721Token.ownerOf(makerAssetId).callAsync(); - expect(newOwner).to.be.bignumber.equal(takerAddress); - }); - }); - }); -}); diff --git a/contracts/extensions/test/global_hooks.ts b/contracts/extensions/test/global_hooks.ts deleted file mode 100644 index 2ca47d433b..0000000000 --- a/contracts/extensions/test/global_hooks.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { env, EnvVars } from '@0x/dev-utils'; - -import { coverage, profiler, provider } from '@0x/contracts-test-utils'; -import { providerUtils } from '@0x/utils'; - -before('start web3 provider', () => { - providerUtils.startProviderEngine(provider); -}); -after('generate coverage report', async () => { - if (env.parseBoolean(EnvVars.SolidityCoverage)) { - const coverageSubprovider = coverage.getCoverageSubproviderSingleton(); - await coverageSubprovider.writeCoverageAsync(); - } - if (env.parseBoolean(EnvVars.SolidityProfiler)) { - const profilerSubprovider = profiler.getProfilerSubproviderSingleton(); - await profilerSubprovider.writeProfilerOutputAsync(); - } - provider.stop(); -}); diff --git a/contracts/extensions/test/order_matcher.ts b/contracts/extensions/test/order_matcher.ts deleted file mode 100644 index 33f0b32745..0000000000 --- a/contracts/extensions/test/order_matcher.ts +++ /dev/null @@ -1,796 +0,0 @@ -import { - artifacts as proxyArtifacts, - ERC20ProxyContract, - ERC20Wrapper, - ERC721ProxyContract, -} from '@0x/contracts-asset-proxy'; -import { DevUtilsContract } from '@0x/contracts-dev-utils'; -import { DummyERC20TokenContract } from '@0x/contracts-erc20'; -import { artifacts as erc721Artifacts, DummyERC721TokenContract } from '@0x/contracts-erc721'; -import { ExchangeContract, ExchangeRevertErrors } from '@0x/contracts-exchange'; -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - expectContractCreationFailedAsync, - LogDecoder, - OrderFactory, - orderUtils, - provider, - sendTransactionResult, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber, providerUtils } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ExchangeFillEventArgs, OrderMatcherContract } from './wrappers'; - -import { artifacts } from './artifacts'; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -chaiSetup.configure(); -const expect = chai.expect; -// tslint:disable:no-unnecessary-type-assertion -describe('OrderMatcher', () => { - let chainId: number; - let makerAddressLeft: string; - let makerAddressRight: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddressLeft: string; - let feeRecipientAddressRight: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let orderMatcher: OrderMatcherContract; - - let erc20BalancesByOwner: ERC20BalancesByOwner; - let erc20Wrapper: ERC20Wrapper; - let orderFactoryLeft: OrderFactory; - let orderFactoryRight: OrderFactory; - - let leftMakerAssetData: string; - let leftTakerAssetData: string; - let defaultERC20MakerAssetAddress: string; - let defaultERC20TakerAssetAddress: string; - - const devUtils = new DevUtilsContract(constants.NULL_ADDRESS, provider, txDefaults); - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - chainId = await providerUtils.getChainIdAsync(provider); - // Create accounts - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - // Hack(albrow): Both Prettier and TSLint insert a trailing comma below - // but that is invalid syntax as of TypeScript version >= 2.8. We don't - // have the right fine-grained configuration options in TSLint, - // Prettier, or TypeScript, to reconcile this, so we will just have to - // wait for them to sort it out. We disable TSLint and Prettier for - // this part of the code for now. This occurs several times in this - // file. See https://github.com/prettier/prettier/issues/4624. - // prettier-ignore - const usedAddresses = ([ - owner, - makerAddressLeft, - makerAddressRight, - takerAddress, - feeRecipientAddressLeft, - // tslint:disable-next-line:trailing-comma - feeRecipientAddressRight - ] = _.slice(accounts, 0, 6)); - // Create wrappers - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - // Deploy ERC20 token & ERC20 proxy - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - // Deploy ERC721 proxy - erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync( - proxyArtifacts.ERC721Proxy, - provider, - txDefaults, - artifacts, - ); - // Depoy exchange - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - artifacts, - await devUtils.encodeERC20AssetData(zrxToken.address).callAsync(), - new BigNumber(chainId), - ); - await exchange.registerAssetProxy(erc20Proxy.address).awaitTransactionSuccessAsync({ - from: owner, - }); - await exchange.registerAssetProxy(erc721Proxy.address).awaitTransactionSuccessAsync({ - from: owner, - }); // Authorize ERC20 trades by exchange - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress(exchange.address).sendTransactionAsync({ - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Deploy OrderMatcher - orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync( - artifacts.OrderMatcher, - provider, - txDefaults, - artifacts, - exchange.address, - ); - // Set default addresses - defaultERC20MakerAssetAddress = erc20TokenA.address; - defaultERC20TakerAssetAddress = erc20TokenB.address; - leftMakerAssetData = await devUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress).callAsync(); - leftTakerAssetData = await devUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress).callAsync(); - // Set OrderMatcher balances and allowances - await erc20TokenA - .setBalance(orderMatcher.address, constants.INITIAL_ERC20_BALANCE) - .awaitTransactionSuccessAsync({ from: owner }, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }); - - await erc20TokenB - .setBalance(orderMatcher.address, constants.INITIAL_ERC20_BALANCE) - .awaitTransactionSuccessAsync({ from: owner }, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }); - - await orderMatcher - .approveAssetProxy(leftMakerAssetData, constants.INITIAL_ERC20_ALLOWANCE) - .awaitTransactionSuccessAsync({}, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }); - - await orderMatcher - .approveAssetProxy(leftTakerAssetData, constants.INITIAL_ERC20_ALLOWANCE) - .awaitTransactionSuccessAsync({}, { pollingIntervalMs: constants.AWAIT_TRANSACTION_MINED_MS }); - - // Create default order parameters - const defaultOrderParamsLeft = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressLeft, - makerAssetData: leftMakerAssetData, - takerAssetData: leftTakerAssetData, - feeRecipientAddress: feeRecipientAddressLeft, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - exchangeAddress: exchange.address, - chainId, - }; - const defaultOrderParamsRight = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressRight, - makerAssetData: leftTakerAssetData, - takerAssetData: leftMakerAssetData, - feeRecipientAddress: feeRecipientAddressRight, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - exchangeAddress: exchange.address, - chainId, - }; - const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; - orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); - const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; - orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('constructor', () => { - it('should revert if assetProxy is unregistered', async () => { - const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - artifacts, - constants.NULL_BYTES, - new BigNumber(chainId), - ); - return expectContractCreationFailedAsync( - (OrderMatcherContract.deployFrom0xArtifactAsync( - artifacts.OrderMatcher, - provider, - txDefaults, - artifacts, - exchangeInstance.address, - ) as any) as sendTransactionResult, - RevertReason.UnregisteredAssetProxy, - ); - }); - }); - describe('matchOrders', () => { - beforeEach(async () => { - erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); - }); - it('should revert if not called by owner', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - const tx = web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: takerAddress, - gas: constants.MAX_MATCH_ORDERS_GAS, - }); - return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); - }); - it('should transfer the correct amounts when orders completely fill each other', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - }); - it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance); - }); - it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - // Match signedOrderLeft with signedOrderRight - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were never partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - const logDecoder = new LogDecoder(web3Wrapper, artifacts); - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - ); - const fillLogs = _.filter( - txReceipt.logs, - log => (log as LogWithDecodedArgs).event === 'Fill', - ); - // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. - expect(fillLogs.length).to.be.equal(2); - }); - it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were initially partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - let params = orderUtils.createFill(signedOrderLeft, signedOrderLeft.takerAssetAmount.dividedToIntegerBy(5)); - await exchange - .fillOrder(params.order, params.takerAssetFillAmount, params.signature) - .awaitTransactionSuccessAsync({ from: takerAddress }); - params = orderUtils.createFill(signedOrderRight, signedOrderRight.takerAssetAmount.dividedToIntegerBy(5)); - await exchange - .fillOrder(params.order, params.takerAssetFillAmount, params.signature) - .awaitTransactionSuccessAsync({ from: takerAddress }); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - const logDecoder = new LogDecoder(web3Wrapper, artifacts); - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - ); - const fillLogs = _.filter( - txReceipt.logs, - log => (log as LogWithDecodedArgs).event === 'Fill', - ); - // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. - expect(fillLogs.length).to.be.equal(2); - }); - it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18), - }); - const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18); - const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount - .times(signedOrderRight.makerAssetAmount) - .dividedToIntegerBy(signedOrderRight.takerAssetAmount); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount), - amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT, - leftTakerAssetSpreadAmount, - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - // Match signedOrderLeft with signedOrderRight - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - // Match signedOrderLeft with signedOrderRight - signedOrderRight.makerAssetData = constants.NULL_BYTES; - signedOrderRight.takerAssetData = constants.NULL_BYTES; - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf(orderMatcher.address).callAsync(); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it('should revert with the correct reason if matchOrders call reverts', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`; - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - const expectedError = new ExchangeRevertErrors.SignatureError(); - const tx = web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }); - return expect(tx).to.revertWith(expectedError); - }); - it('should revert with the correct reason if fillOrder call reverts', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Matcher will not have enough allowance to fill rightOrder - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy(leftMakerAssetData, constants.ZERO_AMOUNT).sendTransactionAsync({ - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const data = exchange - .matchOrders(signedOrderLeft, signedOrderRight, signedOrderLeft.signature, signedOrderRight.signature) - .getABIEncodedTransactionData(); - const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(); - const tx = web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }); - return expect(tx).to.revertWith(expectedError); - }); - }); - describe('withdrawAsset', () => { - it('should allow owner to withdraw ERC20 tokens', async () => { - const erc20AWithdrawAmount = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.withdrawAsset(leftMakerAssetData, erc20AWithdrawAmount).sendTransactionAsync({ - from: owner, - }), - ); - const newBalance = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); - }); - it('should allow owner to withdraw ERC721 tokens', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - erc721Artifacts.DummyERC721Token, - provider, - txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const tokenId = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Token.mint(orderMatcher.address, tokenId).sendTransactionAsync({ from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const assetData = await devUtils.encodeERC721AssetData(erc721Token.address, tokenId).callAsync(); - const withdrawAmount = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.withdrawAsset(assetData, withdrawAmount).sendTransactionAsync({ from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const erc721Owner = await erc721Token.ownerOf(tokenId).callAsync(); - expect(erc721Owner).to.be.equal(owner); - }); - it('should revert if not called by owner', async () => { - const erc20AWithdrawAmount = await erc20TokenA.balanceOf(orderMatcher.address).callAsync(); - expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); - const tx = orderMatcher.withdrawAsset(leftMakerAssetData, erc20AWithdrawAmount).sendTransactionAsync({ - from: takerAddress, - }); - return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); - }); - }); - describe('approveAssetProxy', () => { - it('should be able to set an allowance for ERC20 tokens', async () => { - const allowance = new BigNumber(55465465426546); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy(leftMakerAssetData, allowance).sendTransactionAsync({ - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newAllowance = await erc20TokenA.allowance(orderMatcher.address, erc20Proxy.address).callAsync(); - expect(newAllowance).to.be.bignumber.equal(allowance); - }); - it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - erc721Artifacts.DummyERC721Token, - provider, - txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const assetData = await devUtils - .encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT) - .callAsync(); - const allowance = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy(assetData, allowance).sendTransactionAsync({ from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isApproved = await erc721Token - .isApprovedForAll(orderMatcher.address, erc721Proxy.address) - .callAsync(); - expect(isApproved).to.be.equal(true); - }); - it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - erc721Artifacts.DummyERC721Token, - provider, - txDefaults, - artifacts, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const assetData = await devUtils - .encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT) - .callAsync(); - const allowance = new BigNumber(2); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy(assetData, allowance).sendTransactionAsync({ from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isApproved = await erc721Token - .isApprovedForAll(orderMatcher.address, erc721Proxy.address) - .callAsync(); - expect(isApproved).to.be.equal(true); - }); - it('should revert if not called by owner', async () => { - const approval = new BigNumber(1); - const tx = orderMatcher.approveAssetProxy(leftMakerAssetData, approval).sendTransactionAsync({ - from: takerAddress, - }); - return expect(tx).to.revertWith(RevertReason.OnlyContractOwner); - }); - }); -}); -// tslint:disable:max-file-line-count -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/test/utils/balance_threshold_wrapper.ts b/contracts/extensions/test/utils/balance_threshold_wrapper.ts deleted file mode 100644 index 6305bec56c..0000000000 --- a/contracts/extensions/test/utils/balance_threshold_wrapper.ts +++ /dev/null @@ -1,253 +0,0 @@ -import { ExchangeContract } from '@0x/contracts-exchange'; -import { - FillResults, - formatters, - LogDecoder, - OrderInfo, - orderUtils, - TransactionFactory, - Web3ProviderEngine, -} from '@0x/contracts-test-utils'; -import { SignedOrder } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; - -import { BalanceThresholdFilterContract } from './wrappers'; - -import { artifacts } from './artifacts'; - -export class BalanceThresholdWrapper { - private readonly _balanceThresholdFilter: BalanceThresholdFilterContract; - private readonly _signerTransactionFactory: TransactionFactory; - private readonly _exchange: ExchangeContract; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _logDecoder: LogDecoder; - constructor( - balanceThresholdFilter: BalanceThresholdFilterContract, - exchangeContract: ExchangeContract, - signerTransactionFactory: TransactionFactory, - provider: Web3ProviderEngine, - ) { - this._balanceThresholdFilter = balanceThresholdFilter; - this._exchange = exchangeContract; - this._signerTransactionFactory = signerTransactionFactory; - this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); - } - public async fillOrderAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const data = this._exchange - .fillOrder(params.order, params.takerAssetFillAmount, params.signature) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async fillOrKillOrderAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const data = this._exchange - .fillOrKillOrder(params.order, params.takerAssetFillAmount, params.signature) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async fillOrderNoThrowAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {}, - ): Promise { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const data = this._exchange - .fillOrderNoThrow(params.order, params.takerAssetFillAmount, params.signature) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from, opts.gas); - return txReceipt; - } - public async batchFillOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[] } = {}, - ): Promise { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const data = this._exchange - .batchFillOrders(params.orders, params.takerAssetFillAmounts, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async batchFillOrKillOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[] } = {}, - ): Promise { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const data = this._exchange - .batchFillOrKillOrders(params.orders, params.takerAssetFillAmounts, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async batchFillOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {}, - ): Promise { - const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); - const data = this._exchange - .batchFillOrKillOrders(params.orders, params.takerAssetFillAmounts, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from, opts.gas); - return txReceipt; - } - public async marketSellOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmount: BigNumber }, - ): Promise { - const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); - const data = this._exchange - .marketSellOrders(params.orders, params.takerAssetFillAmount, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async marketSellOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { takerAssetFillAmount: BigNumber; gas?: number }, - ): Promise { - const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); - const data = this._exchange - .marketSellOrdersNoThrow(params.orders, params.takerAssetFillAmount, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from, opts.gas); - return txReceipt; - } - public async marketBuyOrdersAsync( - orders: SignedOrder[], - from: string, - opts: { makerAssetFillAmount: BigNumber }, - ): Promise { - const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); - const data = this._exchange - .marketBuyOrders(params.orders, params.makerAssetFillAmount, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async marketBuyOrdersNoThrowAsync( - orders: SignedOrder[], - from: string, - opts: { makerAssetFillAmount: BigNumber; gas?: number }, - ): Promise { - const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); - const data = this._exchange - .marketBuyOrdersNoThrow(params.orders, params.makerAssetFillAmount, params.signatures) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from, opts.gas); - return txReceipt; - } - public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise { - const params = orderUtils.createCancel(signedOrder); - const data = this._exchange.cancelOrder(params.order).getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async batchCancelOrdersAsync( - orders: SignedOrder[], - from: string, - ): Promise { - const params = formatters.createBatchCancel(orders); - const data = this._exchange.batchCancelOrders(params.orders).getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise { - const data = this._exchange.cancelOrdersUpTo(salt).getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise { - const filledAmount = await this._exchange.filled(orderHashHex).callAsync(); - return filledAmount; - } - public async isCancelledAsync(orderHashHex: string): Promise { - const isCancelled = await this._exchange.cancelled(orderHashHex).callAsync(); - return isCancelled; - } - public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise { - const orderEpoch = await this._exchange.orderEpoch(makerAddress, senderAddress).callAsync(); - return orderEpoch; - } - public async getOrderInfoAsync(signedOrder: SignedOrder): Promise { - const orderInfo = await this._exchange.getOrderInfo(signedOrder).callAsync(); - return orderInfo; - } - public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise { - const ordersInfo = (await this._exchange.getOrdersInfo(signedOrders)).callAsync() as OrderInfo[]; - return ordersInfo; - } - public async matchOrdersAsync( - signedOrderLeft: SignedOrder, - signedOrderRight: SignedOrder, - from: string, - ): Promise { - const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight); - const data = this._exchange - .matchOrders(params.left, params.right, params.leftSignature, params.rightSignature) - .getABIEncodedTransactionData(); - const txReceipt = this._executeTransactionAsync(data, from); - return txReceipt; - } - public async getFillOrderResultsAsync( - signedOrder: SignedOrder, - from: string, - opts: { takerAssetFillAmount?: BigNumber } = {}, - ): Promise { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const fillResults = await this._exchange - .fillOrder(params.order, params.takerAssetFillAmount, params.signature) - .callAsync({ from }); - return fillResults; - } - public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string { - const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); - const data = this._exchange - .fillOrder(params.order, params.takerAssetFillAmount, params.signature) - .getABIEncodedTransactionData(); - return data; - } - public getBalanceThresholdAddress(): string { - return this._balanceThresholdFilter.address; - } - public getExchangeAddress(): string { - return this._exchange.address; - } - private async _executeTransactionAsync( - abiEncodedExchangeTxData: string, - from: string, - gas?: number, - ): Promise { - const signedExchangeTx = this._signerTransactionFactory.newSignedTransaction(abiEncodedExchangeTxData); - const txOpts = gas === undefined ? { from } : { from, gas }; - const txHash = await this._balanceThresholdFilter - .executeTransaction( - signedExchangeTx.salt, - signedExchangeTx.signerAddress, - signedExchangeTx.data, - signedExchangeTx.signature, - txOpts, - ) - .sendTransactionAsync(); - const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return txReceipt; - } -} diff --git a/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts b/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts deleted file mode 100644 index 3e98c6a94a..0000000000 --- a/contracts/extensions/test/utils/dutch_auction_test_wrapper.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { artifacts as erc721Artifacts } from '@0x/contracts-erc721'; -import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange'; -import { LogDecoder, Web3ProviderEngine } from '@0x/contracts-test-utils'; -import { DutchAuctionDetails, SignedOrder } from '@0x/types'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction'; -import { artifacts } from '../../src/artifacts'; - -export class DutchAuctionTestWrapper { - private readonly _dutchAuctionContract: DutchAuctionContract; - private readonly _web3Wrapper: Web3Wrapper; - private readonly _logDecoder: LogDecoder; - - constructor(contractInstance: DutchAuctionContract, provider: Web3ProviderEngine) { - this._dutchAuctionContract = contractInstance; - this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, { - ...artifacts, - ...exchangeArtifacts, - ...erc721Artifacts, - }); - } - /** - * Matches the buy and sell orders at an amount given the following: the current block time, the auction - * start time and the auction begin amount. The sell order is a an order at the lowest amount - * at the end of the auction. Excess from the match is transferred to the seller. - * Over time the price moves from beginAmount to endAmount given the current block.timestamp. - * @param buyOrder The Buyer's order. This order is for the current expected price of the auction. - * @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction). - * @param from Address the transaction is being sent from. - * @return Transaction receipt with decoded logs. - */ - public async matchOrdersAsync( - buyOrder: SignedOrder, - sellOrder: SignedOrder, - from: string, - ): Promise { - const txHash = await this._dutchAuctionContract - .matchOrders(buyOrder, sellOrder, buyOrder.signature, sellOrder.signature) - .sendTransactionAsync({ - from, - }); - const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - return tx; - } - /** - * Calculates the Auction Details for the given order - * @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction). - * @return The dutch auction details. - */ - public async getAuctionDetailsAsync(sellOrder: SignedOrder): Promise { - const auctionDetails = await this._dutchAuctionContract.getAuctionDetails(sellOrder).callAsync(); - return auctionDetails; - } -} diff --git a/contracts/extensions/test/utils/index.ts b/contracts/extensions/test/utils/index.ts deleted file mode 100644 index f188c53b41..0000000000 --- a/contracts/extensions/test/utils/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as ethAbi from 'ethereumjs-abi'; -import * as ethUtil from 'ethereumjs-util'; - -export * from './balance_threshold_wrapper'; -export * from './dutch_auction_test_wrapper'; - -// tslint:disable-next-line:completed-docs -export function encodeDutchAuctionAssetData( - assetData: string, - beginTimeSeconds: BigNumber, - beginAmount: BigNumber, -): string { - const assetDataBuffer = ethUtil.toBuffer(assetData); - const abiEncodedAuctionData = (ethAbi as any).rawEncode( - ['uint256', 'uint256'], - [beginTimeSeconds.toString(), beginAmount.toString()], - ); - const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData); - const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]); - const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer); - return dutchAuctionData; -} diff --git a/contracts/extensions/test/wrappers.ts b/contracts/extensions/test/wrappers.ts new file mode 100644 index 0000000000..4c946b6949 --- /dev/null +++ b/contracts/extensions/test/wrappers.ts @@ -0,0 +1,9 @@ +/* + * ----------------------------------------------------------------------------- + * Warning: This file is auto-generated by contracts-gen. Don't edit manually. + * ----------------------------------------------------------------------------- + */ +export * from '../test/generated-wrappers/lib_asset_data_transfer'; +export * from '../test/generated-wrappers/lib_asset_data_transfer_rich_errors'; +export * from '../test/generated-wrappers/lib_weth_utils_rich_errors'; +export * from '../test/generated-wrappers/mixin_weth_utils'; diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index 73784c22b3..0f5c6dd95c 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -3,12 +3,14 @@ "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "files": [ - "generated-artifacts/BalanceThresholdFilter.json", - "generated-artifacts/DutchAuction.json", - "generated-artifacts/Exchange.json", - "generated-artifacts/ExchangeWrapper.json", - "generated-artifacts/OrderMatcher.json", - "generated-artifacts/WETH9.json" + "generated-artifacts/LibAssetDataTransfer.json", + "generated-artifacts/LibAssetDataTransferRichErrors.json", + "generated-artifacts/LibWethUtilsRichErrors.json", + "generated-artifacts/MixinWethUtils.json", + "test/generated-artifacts/LibAssetDataTransfer.json", + "test/generated-artifacts/LibAssetDataTransferRichErrors.json", + "test/generated-artifacts/LibWethUtilsRichErrors.json", + "test/generated-artifacts/MixinWethUtils.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/integrations/package.json b/contracts/integrations/package.json index 10950db5c2..4ded8a52ea 100644 --- a/contracts/integrations/package.json +++ b/contracts/integrations/package.json @@ -59,6 +59,7 @@ "@0x/contracts-dev-utils": "^1.0.5", "@0x/contracts-exchange-forwarder": "^4.0.5", "@0x/contracts-exchange-libs": "^4.1.1", + "@0x/contracts-extensions": "^5.1.4", "@0x/contracts-gen": "^2.0.5", "@0x/contracts-utils": "^4.2.0", "@0x/coordinator-server": "^1.0.5", diff --git a/contracts/integrations/test/forwarder/forwarder_test.ts b/contracts/integrations/test/forwarder/forwarder_test.ts index a58e1aa2fd..856e0b9364 100644 --- a/contracts/integrations/test/forwarder/forwarder_test.ts +++ b/contracts/integrations/test/forwarder/forwarder_test.ts @@ -3,6 +3,7 @@ import { DummyERC20TokenContract } from '@0x/contracts-erc20'; import { DummyERC721TokenContract } from '@0x/contracts-erc721'; import { artifacts as exchangeArtifacts, ExchangeContract } from '@0x/contracts-exchange'; import { artifacts, ExchangeForwarderRevertErrors, ForwarderContract } from '@0x/contracts-exchange-forwarder'; +import { MixinWethUtilsRevertErrors } from '@0x/contracts-extensions'; import { blockchainTests, constants, @@ -426,7 +427,7 @@ blockchainTests('Forwarder integration tests', env => { }); const forwarderFeeAmounts = [toBaseUnitAmount(0.2)]; const forwarderFeeRecipientAddresses: string[] = []; - const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError( + const revertError = new MixinWethUtilsRevertErrors.EthFeeLengthMismatchError( new BigNumber(forwarderFeeAmounts.length), new BigNumber(forwarderFeeRecipientAddresses.length), ); @@ -443,7 +444,7 @@ blockchainTests('Forwarder integration tests', env => { }); const forwarderFeeAmounts: BigNumber[] = []; const forwarderFeeRecipientAddresses = [randomAddress()]; - const revertError = new ExchangeForwarderRevertErrors.EthFeeLengthMismatchError( + const revertError = new MixinWethUtilsRevertErrors.EthFeeLengthMismatchError( new BigNumber(forwarderFeeAmounts.length), new BigNumber(forwarderFeeRecipientAddresses.length), ); @@ -768,7 +769,7 @@ blockchainTests('Forwarder integration tests', env => { const order = await maker.signOrderAsync(); const forwarderFeeAmounts = [toBaseUnitAmount(1)]; const value = forwarderFeeAmounts[0].minus(1); - const revertError = new ExchangeForwarderRevertErrors.InsufficientEthForFeeError( + const revertError = new MixinWethUtilsRevertErrors.InsufficientEthForFeeError( forwarderFeeAmounts[0], value, ); diff --git a/package.json b/package.json index c837e75ce5..b8033a3add 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "install:all": "yarn install", "wsrun": "wsrun", "lerna": "lerna", - "build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing --exclude @0x/contracts-extensions", - "build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing --exclude @0x/contracts-extensions", - "build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing --exclude @0x/contracts-extensions", + "build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing", + "build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing", + "build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing", "build:monorepo_scripts": "PKG=@0x/monorepo-scripts yarn build", "build:ts": "tsc -b", "watch:ts": "tsc -b -w", @@ -39,7 +39,7 @@ "contracts:watch": "wsrun watch $PKG --parallel --exclude-missing", "remove_node_modules": "lerna clean --yes; rm -rf node_modules", "rebuild": "run-s clean build", - "test": "wsrun test $PKG --fast-exit --serial --exclude-missing --exclude @0x/asset-swapper --exclude @0x/orderbook --exclude @0x/contracts-extensions", + "test": "wsrun test $PKG --fast-exit --serial --exclude-missing --exclude @0x/asset-swapper --exclude @0x/orderbook", "test:contracts": "wsrun test -p ${npm_package_config_contractsPackages} -c --fast-exit --exclude-missing", "generate_doc": "node ./packages/monorepo-scripts/lib/doc_generate.js", "upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun s3:sync_md_docs --exclude-missing", diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 66b6f4dc77..b05fa965b2 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -3,7 +3,7 @@ "version": "5.3.0", "changes": [ { - "note": "Added Broker revert errors", + "note": "Added Broker, MixinWethUtils revert errors", "pr": 2455 }, { diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 030c91f244..a6bb067169 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -30,9 +30,10 @@ export { export import BrokerRevertErrors = require('./revert_errors/broker/revert_errors'); export import CoordinatorRevertErrors = require('./revert_errors/coordinator/revert_errors'); export import ExchangeForwarderRevertErrors = require('./revert_errors/exchange-forwarder/revert_errors'); -export import LibAssetDataTransferRevertErrors = require('./revert_errors/exchange-libs/lib_asset_data_transfer_revert_errors'); export import LibMathRevertErrors = require('./revert_errors/exchange-libs/lib_math_revert_errors'); export import ExchangeRevertErrors = require('./revert_errors/exchange/revert_errors'); +export import LibAssetDataTransferRevertErrors = require('./revert_errors/extensions/lib_asset_data_transfer_revert_errors'); +export import MixinWethUtilsRevertErrors = require('./revert_errors/extensions/mixin_weth_utils_revert_errors'); export import FixedMathRevertErrors = require('./revert_errors/staking/fixed_math_revert_errors'); export import StakingRevertErrors = require('./revert_errors/staking/staking_revert_errors'); export import AuthorizableRevertErrors = require('./revert_errors/utils/authorizable_revert_errors'); diff --git a/packages/utils/src/revert_errors/exchange-forwarder/revert_errors.ts b/packages/utils/src/revert_errors/exchange-forwarder/revert_errors.ts index ad8035b71b..03dc953de8 100644 --- a/packages/utils/src/revert_errors/exchange-forwarder/revert_errors.ts +++ b/packages/utils/src/revert_errors/exchange-forwarder/revert_errors.ts @@ -28,16 +28,6 @@ export class UnsupportedFeeError extends RevertError { } } -export class InsufficientEthForFeeError extends RevertError { - constructor(ethFeeRequired?: BigNumber | number | string, ethAvailable?: BigNumber | number | string) { - super( - 'InsufficientEthForFeeError', - 'InsufficientEthForFeeError(uint256 ethFeeRequired, uint256 ethAvailable)', - { ethFeeRequired, ethAvailable }, - ); - } -} - export class OverspentWethError extends RevertError { constructor(wethSpent?: BigNumber | number | string, msgValue?: BigNumber | number | string) { super('OverspentWethError', 'OverspentWethError(uint256 wethSpent, uint256 msgValue)', { @@ -47,42 +37,18 @@ export class OverspentWethError extends RevertError { } } -export class DefaultFunctionWethContractOnlyError extends RevertError { - constructor(senderAddress?: string) { - super('DefaultFunctionWethContractOnlyError', 'DefaultFunctionWethContractOnlyError(address senderAddress)', { - senderAddress, - }); - } -} - export class MsgValueCannotEqualZeroError extends RevertError { constructor() { super('MsgValueCannotEqualZeroError', 'MsgValueCannotEqualZeroError()', {}); } } -export class EthFeeLengthMismatchError extends RevertError { - constructor(ethFeesLength?: BigNumber | number | string, feeRecipientsLength?: BigNumber | number | string) { - super( - 'EthFeeLengthMismatchError', - 'EthFeeLengthMismatchError(uint256 ethFeesLength, uint256 feeRecipientsLength)', - { - ethFeesLength, - feeRecipientsLength, - }, - ); - } -} - const types = [ UnregisteredAssetProxyError, CompleteBuyFailedError, UnsupportedFeeError, - InsufficientEthForFeeError, OverspentWethError, - DefaultFunctionWethContractOnlyError, MsgValueCannotEqualZeroError, - EthFeeLengthMismatchError, ]; // Register the types we've defined. diff --git a/packages/utils/src/revert_errors/exchange-libs/lib_asset_data_transfer_revert_errors.ts b/packages/utils/src/revert_errors/extensions/lib_asset_data_transfer_revert_errors.ts similarity index 100% rename from packages/utils/src/revert_errors/exchange-libs/lib_asset_data_transfer_revert_errors.ts rename to packages/utils/src/revert_errors/extensions/lib_asset_data_transfer_revert_errors.ts diff --git a/packages/utils/src/revert_errors/extensions/mixin_weth_utils_revert_errors.ts b/packages/utils/src/revert_errors/extensions/mixin_weth_utils_revert_errors.ts new file mode 100644 index 0000000000..79d85c5a56 --- /dev/null +++ b/packages/utils/src/revert_errors/extensions/mixin_weth_utils_revert_errors.ts @@ -0,0 +1,48 @@ +import { BigNumber } from '../../configured_bignumber'; +import { RevertError } from '../../revert_error'; + +// tslint:disable:max-classes-per-file + +export class UnregisteredAssetProxyError extends RevertError { + constructor() { + super('UnregisteredAssetProxyError', 'UnregisteredAssetProxyError()', {}); + } +} + +export class InsufficientEthForFeeError extends RevertError { + constructor(ethFeeRequired?: BigNumber | number | string, ethAvailable?: BigNumber | number | string) { + super( + 'InsufficientEthForFeeError', + 'InsufficientEthForFeeError(uint256 ethFeeRequired, uint256 ethAvailable)', + { ethFeeRequired, ethAvailable }, + ); + } +} + +export class DefaultFunctionWethContractOnlyError extends RevertError { + constructor(senderAddress?: string) { + super('DefaultFunctionWethContractOnlyError', 'DefaultFunctionWethContractOnlyError(address senderAddress)', { + senderAddress, + }); + } +} + +export class EthFeeLengthMismatchError extends RevertError { + constructor(ethFeesLength?: BigNumber | number | string, feeRecipientsLength?: BigNumber | number | string) { + super( + 'EthFeeLengthMismatchError', + 'EthFeeLengthMismatchError(uint256 ethFeesLength, uint256 feeRecipientsLength)', + { + ethFeesLength, + feeRecipientsLength, + }, + ); + } +} + +const types = [InsufficientEthForFeeError, DefaultFunctionWethContractOnlyError, EthFeeLengthMismatchError]; + +// Register the types we've defined. +for (const type of types) { + RevertError.registerType(type); +}