aboutsummaryrefslogtreecommitdiffstats
path: root/contracts/core
diff options
context:
space:
mode:
authorLeonid Logvinov <logvinov.leon@gmail.com>2018-12-11 02:53:25 +0800
committerLeonid Logvinov <logvinov.leon@gmail.com>2018-12-11 02:53:25 +0800
commit924c2705b28f5821fd36d653fb3f7aa7bb015caf (patch)
tree99bd6a5ba4bc18c5fabbd40122bfec0f859e7de7 /contracts/core
parentb88ff0a19ff7695086107cbcffe980dc4ad84e10 (diff)
downloaddexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar.gz
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar.bz2
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar.lz
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar.xz
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.tar.zst
dexon-sol-tools-924c2705b28f5821fd36d653fb3f7aa7bb015caf.zip
Refactor our extensions package
Diffstat (limited to 'contracts/core')
-rw-r--r--contracts/core/README.md2
-rw-r--r--contracts/core/compiler.json3
-rw-r--r--contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol205
-rw-r--r--contracts/core/contracts/extensions/Forwarder/Forwarder.sol50
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinAssets.sol143
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol260
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol214
-rw-r--r--contracts/core/contracts/extensions/Forwarder/MixinWeth.sol113
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol34
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol30
-rw-r--r--contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol80
-rw-r--r--contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol62
-rw-r--r--contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol34
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol53
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol87
-rw-r--r--contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol41
-rw-r--r--contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol218
-rw-r--r--contracts/core/package.json2
-rw-r--r--contracts/core/src/artifacts/index.ts6
-rw-r--r--contracts/core/src/index.ts1
-rw-r--r--contracts/core/src/wrappers/index.ts3
-rw-r--r--contracts/core/test/exchange/signature_validator.ts8
-rw-r--r--contracts/core/test/exchange/transactions.ts7
-rw-r--r--contracts/core/test/extensions/dutch_auction.ts455
-rw-r--r--contracts/core/test/extensions/forwarder.ts1288
-rw-r--r--contracts/core/test/extensions/order_validator.ts606
-rw-r--r--contracts/core/test/tutorials/arbitrage.ts260
-rw-r--r--contracts/core/test/utils/forwarder_wrapper.ts119
-rw-r--r--contracts/core/test/utils/index.ts3
-rw-r--r--contracts/core/tsconfig.json3
30 files changed, 12 insertions, 4378 deletions
diff --git a/contracts/core/README.md b/contracts/core/README.md
index 922ea21ad..d055705c2 100644
--- a/contracts/core/README.md
+++ b/contracts/core/README.md
@@ -8,8 +8,6 @@ Contracts that make up and interact with version 2.0.0 of the protocol can be fo
* [protocol](./contracts/protocol)
* This directory contains the contracts that make up version 2.0.0. A full specification can be found [here](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md).
-* [extensions](./contracts/extensions)
- * This directory contains contracts that interact with the 2.0.0 contracts and will be used in production, such as the [Forwarder](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md) contract.
* [test](./contracts/test)
* This directory contains mocks and other contracts that are used solely for testing contracts within the other directories.
diff --git a/contracts/core/compiler.json b/contracts/core/compiler.json
index 3f869024c..10e5bb0a1 100644
--- a/contracts/core/compiler.json
+++ b/contracts/core/compiler.json
@@ -20,14 +20,11 @@
},
"contracts": [
"AssetProxyOwner",
- "DutchAuction",
"ERC20Proxy",
"ERC721Proxy",
"Exchange",
- "Forwarder",
"MixinAuthorizable",
"MultiAssetProxy",
- "OrderValidator",
"TestAssetProxyOwner",
"TestAssetProxyDispatcher",
"TestExchangeInternals",
diff --git a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol b/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol
deleted file mode 100644
index 9c9f3990a..000000000
--- a/contracts/core/contracts/extensions/DutchAuction/DutchAuction.sol
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol";
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol";
-import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
-import "@0x/contracts-utils/contracts/utils/SafeMath/SafeMath.sol";
-
-
-contract DutchAuction is
- SafeMath
-{
- using LibBytes for bytes;
-
- // 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 = safeSub(buyOrder.makerAssetAmount, auctionDetails.currentAmount);
- uint256 sellerExcessAmount = safeSub(leftMakerAssetSpreadAmount, 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 = safeAdd(
- minAmount,
- safeDiv(
- safeMul(remainingDurationSeconds, amountDelta),
- auctionDurationSeconds
- )
- );
- }
- return auctionDetails;
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/Forwarder.sol b/contracts/core/contracts/extensions/Forwarder/Forwarder.sol
deleted file mode 100644
index 94dec40ed..000000000
--- a/contracts/core/contracts/extensions/Forwarder/Forwarder.sol
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "./MixinWeth.sol";
-import "./MixinForwarderCore.sol";
-import "./libs/LibConstants.sol";
-import "./MixinAssets.sol";
-import "./MixinExchangeWrapper.sol";
-
-
-// solhint-disable no-empty-blocks
-contract Forwarder is
- LibConstants,
- MixinWeth,
- MixinAssets,
- MixinExchangeWrapper,
- MixinForwarderCore
-{
- constructor (
- address _exchange,
- bytes memory _zrxAssetData,
- bytes memory _wethAssetData
- )
- public
- LibConstants(
- _exchange,
- _zrxAssetData,
- _wethAssetData
- )
- MixinForwarderCore()
- {}
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol b/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol
deleted file mode 100644
index 3ebf75161..000000000
--- a/contracts/core/contracts/extensions/Forwarder/MixinAssets.sol
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
-import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol";
-import "./libs/LibConstants.sol";
-import "./mixins/MAssets.sol";
-
-
-contract MixinAssets is
- Ownable,
- LibConstants,
- MAssets
-{
- using LibBytes for bytes;
-
- bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
-
- /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
- /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
- /// used to withdraw assets that were accidentally sent to this contract.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of ERC20 token to withdraw.
- function withdrawAsset(
- bytes assetData,
- uint256 amount
- )
- external
- onlyOwner
- {
- transferAssetToSender(assetData, amount);
- }
-
- /// @dev Transfers given amount of asset to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferAssetToSender(
- bytes memory assetData,
- uint256 amount
- )
- internal
- {
- bytes4 proxyId = assetData.readBytes4(0);
-
- if (proxyId == ERC20_DATA_ID) {
- transferERC20Token(assetData, amount);
- } else if (proxyId == ERC721_DATA_ID) {
- transferERC721Token(assetData, amount);
- } else {
- revert("UNSUPPORTED_ASSET_PROXY");
- }
- }
-
- /// @dev Decodes ERC20 assetData and transfers given amount to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferERC20Token(
- bytes memory assetData,
- uint256 amount
- )
- internal
- {
- address token = assetData.readAddress(16);
-
- // Transfer tokens.
- // We do a raw call so we can check the success separate
- // from the return data.
- bool success = token.call(abi.encodeWithSelector(
- ERC20_TRANSFER_SELECTOR,
- msg.sender,
- amount
- ));
- require(
- success,
- "TRANSFER_FAILED"
- );
-
- // Check return data.
- // If there is no return data, we assume the token incorrectly
- // does not return a bool. In this case we expect it to revert
- // on failure, which was handled above.
- // If the token does return data, we require that it is a single
- // value that evaluates to true.
- assembly {
- if returndatasize {
- success := 0
- if eq(returndatasize, 32) {
- // First 64 bytes of memory are reserved scratch space
- returndatacopy(0, 0, 32)
- success := mload(0)
- }
- }
- }
- require(
- success,
- "TRANSFER_FAILED"
- );
- }
-
- /// @dev Decodes ERC721 assetData and transfers given amount to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferERC721Token(
- bytes memory assetData,
- uint256 amount
- )
- internal
- {
- require(
- amount == 1,
- "INVALID_AMOUNT"
- );
- // Decode asset data.
- address token = assetData.readAddress(16);
- uint256 tokenId = assetData.readUint256(36);
-
- // Perform transfer.
- IERC721Token(token).transferFrom(
- address(this),
- msg.sender,
- tokenId
- );
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol b/contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol
deleted file mode 100644
index 210eb14c2..000000000
--- a/contracts/core/contracts/extensions/Forwarder/MixinExchangeWrapper.sol
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "./libs/LibConstants.sol";
-import "./mixins/MExchangeWrapper.sol";
-import "@0x/contracts-libs/contracts/libs/LibAbiEncoder.sol";
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
-import "@0x/contracts-libs/contracts/libs/LibMath.sol";
-
-
-contract MixinExchangeWrapper is
- LibAbiEncoder,
- LibFillResults,
- LibMath,
- LibConstants,
- MExchangeWrapper
-{
- /// @dev Fills the input order.
- /// Returns false if the transaction would otherwise revert.
- /// @param order Order struct containing order specifications.
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
- /// @param signature Proof that order has been created by maker.
- /// @return Amounts filled and fees paid by maker and taker.
- function fillOrderNoThrow(
- LibOrder.Order memory order,
- uint256 takerAssetFillAmount,
- bytes memory signature
- )
- internal
- returns (FillResults memory fillResults)
- {
- // ABI encode calldata for `fillOrder`
- bytes memory fillOrderCalldata = abiEncodeFillOrder(
- order,
- takerAssetFillAmount,
- signature
- );
-
- address exchange = address(EXCHANGE);
-
- // Call `fillOrder` and handle any exceptions gracefully
- assembly {
- let success := call(
- gas, // forward all gas
- exchange, // call address of Exchange contract
- 0, // transfer 0 wei
- add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)
- mload(fillOrderCalldata), // length of input
- fillOrderCalldata, // write output over input
- 128 // output size is 128 bytes
- )
- if success {
- mstore(fillResults, mload(fillOrderCalldata))
- mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
- mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
- mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))
- }
- }
- // fillResults values will be 0 by default if call was unsuccessful
- return fillResults;
- }
-
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.
- /// Returns false if the transaction would otherwise revert.
- /// @param orders Array of order specifications.
- /// @param wethSellAmount Desired amount of WETH to sell.
- /// @param signatures Proofs that orders have been signed by makers.
- /// @return Amounts filled and fees paid by makers and taker.
- function marketSellWeth(
- LibOrder.Order[] memory orders,
- uint256 wethSellAmount,
- bytes[] memory signatures
- )
- internal
- returns (FillResults memory totalFillResults)
- {
- bytes memory makerAssetData = orders[0].makerAssetData;
- bytes memory wethAssetData = WETH_ASSET_DATA;
-
- uint256 ordersLength = orders.length;
- for (uint256 i = 0; i != ordersLength; i++) {
-
- // We assume that asset being bought by taker is the same for each order.
- // We assume that asset being sold by taker is WETH for each order.
- orders[i].makerAssetData = makerAssetData;
- orders[i].takerAssetData = wethAssetData;
-
- // Calculate the remaining amount of WETH to sell
- uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);
-
- // Attempt to sell the remaining amount of WETH
- FillResults memory singleFillResults = fillOrderNoThrow(
- orders[i],
- remainingTakerAssetFillAmount,
- signatures[i]
- );
-
- // Update amounts filled and fees paid by maker and taker
- addFillResults(totalFillResults, singleFillResults);
-
- // Stop execution if the entire amount of takerAsset has been sold
- if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {
- break;
- }
- }
- return totalFillResults;
- }
-
- /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.
- /// Returns false if the transaction would otherwise revert.
- /// The asset being sold by taker must always be WETH.
- /// @param orders Array of order specifications.
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
- /// @param signatures Proofs that orders have been signed by makers.
- /// @return Amounts filled and fees paid by makers and taker.
- function marketBuyExactAmountWithWeth(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount,
- bytes[] memory signatures
- )
- internal
- returns (FillResults memory totalFillResults)
- {
- bytes memory makerAssetData = orders[0].makerAssetData;
- bytes memory wethAssetData = WETH_ASSET_DATA;
-
- uint256 ordersLength = orders.length;
- for (uint256 i = 0; i != ordersLength; i++) {
-
- // We assume that asset being bought by taker is the same for each order.
- // We assume that asset being sold by taker is WETH for each order.
- orders[i].makerAssetData = makerAssetData;
- orders[i].takerAssetData = wethAssetData;
-
- // Calculate the remaining amount of makerAsset to buy
- uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
-
- // Convert the remaining amount of makerAsset to buy into remaining amount
- // of takerAsset to sell, assuming entire amount can be sold in the current order.
- // We round up because the exchange rate computed by fillOrder rounds in favor
- // of the Maker. In this case we want to overestimate the amount of takerAsset.
- uint256 remainingTakerAssetFillAmount = getPartialAmountCeil(
- orders[i].takerAssetAmount,
- orders[i].makerAssetAmount,
- remainingMakerAssetFillAmount
- );
-
- // Attempt to sell the remaining amount of takerAsset
- FillResults memory singleFillResults = fillOrderNoThrow(
- orders[i],
- remainingTakerAssetFillAmount,
- signatures[i]
- );
-
- // Update amounts filled and fees paid by maker and taker
- addFillResults(totalFillResults, singleFillResults);
-
- // Stop execution if the entire amount of makerAsset has been bought
- uint256 makerAssetFilledAmount = totalFillResults.makerAssetFilledAmount;
- if (makerAssetFilledAmount >= makerAssetFillAmount) {
- break;
- }
- }
-
- require(
- makerAssetFilledAmount >= makerAssetFillAmount,
- "COMPLETE_FILL_FAILED"
- );
- return totalFillResults;
- }
-
- /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee
- /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).
- /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX
- /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
- /// The asset being sold by taker must always be WETH.
- /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.
- /// @param zrxBuyAmount Desired amount of ZRX to buy.
- /// @param signatures Proofs that orders have been created by makers.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function marketBuyExactZrxWithWeth(
- LibOrder.Order[] memory orders,
- uint256 zrxBuyAmount,
- bytes[] memory signatures
- )
- internal
- returns (FillResults memory totalFillResults)
- {
- // Do nothing if zrxBuyAmount == 0
- if (zrxBuyAmount == 0) {
- return totalFillResults;
- }
-
- bytes memory zrxAssetData = ZRX_ASSET_DATA;
- bytes memory wethAssetData = WETH_ASSET_DATA;
- uint256 zrxPurchased = 0;
-
- uint256 ordersLength = orders.length;
- for (uint256 i = 0; i != ordersLength; i++) {
-
- // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.
- orders[i].makerAssetData = zrxAssetData;
- orders[i].takerAssetData = wethAssetData;
-
- // Calculate the remaining amount of ZRX to buy.
- uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);
-
- // Convert the remaining amount of ZRX to buy into remaining amount
- // of WETH to sell, assuming entire amount can be sold in the current order.
- // We round up because the exchange rate computed by fillOrder rounds in favor
- // of the Maker. In this case we want to overestimate the amount of takerAsset.
- uint256 remainingWethSellAmount = getPartialAmountCeil(
- orders[i].takerAssetAmount,
- safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
- remainingZrxBuyAmount
- );
-
- // Attempt to sell the remaining amount of WETH.
- FillResults memory singleFillResult = fillOrderNoThrow(
- orders[i],
- remainingWethSellAmount,
- signatures[i]
- );
-
- // Update amounts filled and fees paid by maker and taker.
- addFillResults(totalFillResults, singleFillResult);
- zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
-
- // Stop execution if the entire amount of ZRX has been bought.
- if (zrxPurchased >= zrxBuyAmount) {
- break;
- }
- }
-
- require(
- zrxPurchased >= zrxBuyAmount,
- "COMPLETE_FILL_FAILED"
- );
- return totalFillResults;
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol b/contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol
deleted file mode 100644
index bab78d79b..000000000
--- a/contracts/core/contracts/extensions/Forwarder/MixinForwarderCore.sol
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "./libs/LibConstants.sol";
-import "./mixins/MWeth.sol";
-import "./mixins/MAssets.sol";
-import "./mixins/MExchangeWrapper.sol";
-import "./interfaces/IForwarderCore.sol";
-import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
-import "@0x/contracts-libs/contracts/libs/LibMath.sol";
-
-
-contract MixinForwarderCore is
- LibFillResults,
- LibMath,
- LibConstants,
- MWeth,
- MAssets,
- MExchangeWrapper,
- IForwarderCore
-{
- using LibBytes for bytes;
-
- /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.
- constructor ()
- public
- {
- address proxyAddress = EXCHANGE.getAssetProxy(ERC20_DATA_ID);
- require(
- proxyAddress != address(0),
- "UNREGISTERED_ASSET_PROXY"
- );
- ETHER_TOKEN.approve(proxyAddress, MAX_UINT);
- ZRX_TOKEN.approve(proxyAddress, MAX_UINT);
- }
-
- /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
- /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
- /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
- /// Any ETH not spent will be refunded to sender.
- /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
- /// @param signatures Proofs that orders have been created by makers.
- /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
- /// @param feeSignatures Proofs that feeOrders have been created by makers.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
- function marketSellOrdersWithEth(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 feePercentage,
- address feeRecipient
- )
- public
- payable
- returns (
- FillResults memory orderFillResults,
- FillResults memory feeOrderFillResults
- )
- {
- // Convert ETH to WETH.
- convertEthToWeth();
-
- uint256 wethSellAmount;
- uint256 zrxBuyAmount;
- uint256 makerAssetAmountPurchased;
- if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
- // Calculate amount of WETH that won't be spent on ETH fees.
- wethSellAmount = getPartialAmountFloor(
- PERCENTAGE_DENOMINATOR,
- safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
- msg.value
- );
- // Market sell available WETH.
- // ZRX fees are paid with this contract's balance.
- orderFillResults = marketSellWeth(
- orders,
- wethSellAmount,
- signatures
- );
- // The fee amount must be deducted from the amount transfered back to sender.
- makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
- } else {
- // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
- wethSellAmount = getPartialAmountFloor(
- MAX_WETH_FILL_PERCENTAGE,
- PERCENTAGE_DENOMINATOR,
- msg.value
- );
- // Market sell 95% of WETH.
- // ZRX fees are payed with this contract's balance.
- orderFillResults = marketSellWeth(
- orders,
- wethSellAmount,
- signatures
- );
- // Buy back all ZRX spent on fees.
- zrxBuyAmount = orderFillResults.takerFeePaid;
- feeOrderFillResults = marketBuyExactZrxWithWeth(
- feeOrders,
- zrxBuyAmount,
- feeSignatures
- );
- makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
- }
-
- // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
- // Refund remaining ETH to msg.sender.
- transferEthFeeAndRefund(
- orderFillResults.takerAssetFilledAmount,
- feeOrderFillResults.takerAssetFilledAmount,
- feePercentage,
- feeRecipient
- );
-
- // Transfer purchased assets to msg.sender.
- transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
- }
-
- /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
- /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
- /// Any ETH not spent will be refunded to sender.
- /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
- /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.
- /// @param signatures Proofs that orders have been created by makers.
- /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
- /// @param feeSignatures Proofs that feeOrders have been created by makers.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
- function marketBuyOrdersWithEth(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 feePercentage,
- address feeRecipient
- )
- public
- payable
- returns (
- FillResults memory orderFillResults,
- FillResults memory feeOrderFillResults
- )
- {
- // Convert ETH to WETH.
- convertEthToWeth();
-
- uint256 zrxBuyAmount;
- uint256 makerAssetAmountPurchased;
- if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
- // If the makerAsset is ZRX, it is not necessary to pay fees out of this
- // contracts's ZRX balance because fees are factored into the price of the order.
- orderFillResults = marketBuyExactZrxWithWeth(
- orders,
- makerAssetFillAmount,
- signatures
- );
- // The fee amount must be deducted from the amount transfered back to sender.
- makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
- } else {
- // Attemp to purchase desired amount of makerAsset.
- // ZRX fees are payed with this contract's balance.
- orderFillResults = marketBuyExactAmountWithWeth(
- orders,
- makerAssetFillAmount,
- signatures
- );
- // Buy back all ZRX spent on fees.
- zrxBuyAmount = orderFillResults.takerFeePaid;
- feeOrderFillResults = marketBuyExactZrxWithWeth(
- feeOrders,
- zrxBuyAmount,
- feeSignatures
- );
- makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
- }
-
- // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
- // Refund remaining ETH to msg.sender.
- transferEthFeeAndRefund(
- orderFillResults.takerAssetFilledAmount,
- feeOrderFillResults.takerAssetFilledAmount,
- feePercentage,
- feeRecipient
- );
-
- // Transfer purchased assets to msg.sender.
- transferAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/MixinWeth.sol b/contracts/core/contracts/extensions/Forwarder/MixinWeth.sol
deleted file mode 100644
index 2a281f3ae..000000000
--- a/contracts/core/contracts/extensions/Forwarder/MixinWeth.sol
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "@0x/contracts-libs/contracts/libs/LibMath.sol";
-import "./libs/LibConstants.sol";
-import "./mixins/MWeth.sol";
-
-
-contract MixinWeth is
- LibMath,
- LibConstants,
- MWeth
-{
- /// @dev Default payabale function, this allows us to withdraw WETH
- function ()
- public
- payable
- {
- require(
- msg.sender == address(ETHER_TOKEN),
- "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"
- );
- }
-
- /// @dev Converts message call's ETH value into WETH.
- function convertEthToWeth()
- internal
- {
- require(
- msg.value > 0,
- "INVALID_MSG_VALUE"
- );
- ETHER_TOKEN.deposit.value(msg.value)();
- }
-
- /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
- /// Refunds any excess ETH to msg.sender.
- /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.
- /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- function transferEthFeeAndRefund(
- uint256 wethSoldExcludingFeeOrders,
- uint256 wethSoldForZrx,
- uint256 feePercentage,
- address feeRecipient
- )
- internal
- {
- // Ensure feePercentage is less than 5%.
- require(
- feePercentage <= MAX_FEE_PERCENTAGE,
- "FEE_PERCENTAGE_TOO_LARGE"
- );
-
- // Ensure that no extra WETH owned by this contract has been sold.
- uint256 wethSold = safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx);
- require(
- wethSold <= msg.value,
- "OVERSOLD_WETH"
- );
-
- // Calculate amount of WETH that hasn't been sold.
- uint256 wethRemaining = safeSub(msg.value, wethSold);
-
- // Calculate ETH fee to pay to feeRecipient.
- uint256 ethFee = getPartialAmountFloor(
- feePercentage,
- PERCENTAGE_DENOMINATOR,
- wethSoldExcludingFeeOrders
- );
-
- // Ensure fee is less than amount of WETH remaining.
- require(
- ethFee <= wethRemaining,
- "INSUFFICIENT_ETH_REMAINING"
- );
-
- // Do nothing if no WETH remaining
- if (wethRemaining > 0) {
- // Convert remaining WETH to ETH
- ETHER_TOKEN.withdraw(wethRemaining);
-
- // Pay ETH to feeRecipient
- if (ethFee > 0) {
- feeRecipient.transfer(ethFee);
- }
-
- // Refund remaining ETH to msg.sender.
- uint256 ethRefund = safeSub(wethRemaining, ethFee);
- if (ethRefund > 0) {
- msg.sender.transfer(ethRefund);
- }
- }
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol
deleted file mode 100644
index 1e034c003..000000000
--- a/contracts/core/contracts/extensions/Forwarder/interfaces/IAssets.sol
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-
-contract IAssets {
-
- /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to
- /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be
- /// used to withdraw assets that were accidentally sent to this contract.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of ERC20 token to withdraw.
- function withdrawAsset(
- bytes assetData,
- uint256 amount
- )
- external;
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol
deleted file mode 100644
index f5a26e2ba..000000000
--- a/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarder.sol
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "./IForwarderCore.sol";
-import "./IAssets.sol";
-
-
-// solhint-disable no-empty-blocks
-contract IForwarder is
- IForwarderCore,
- IAssets
-{}
diff --git a/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol b/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol
deleted file mode 100644
index eede20bb8..000000000
--- a/contracts/core/contracts/extensions/Forwarder/interfaces/IForwarderCore.sol
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
-
-
-contract IForwarderCore {
-
- /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
- /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
- /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
- /// Any ETH not spent will be refunded to sender.
- /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
- /// @param signatures Proofs that orders have been created by makers.
- /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
- /// @param feeSignatures Proofs that feeOrders have been created by makers.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
- function marketSellOrdersWithEth(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 feePercentage,
- address feeRecipient
- )
- public
- payable
- returns (
- LibFillResults.FillResults memory orderFillResults,
- LibFillResults.FillResults memory feeOrderFillResults
- );
-
- /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
- /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
- /// Any ETH not spent will be refunded to sender.
- /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
- /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.
- /// @param signatures Proofs that orders have been created by makers.
- /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
- /// @param feeSignatures Proofs that feeOrders have been created by makers.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
- function marketBuyOrdersWithEth(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 feePercentage,
- address feeRecipient
- )
- public
- payable
- returns (
- LibFillResults.FillResults memory orderFillResults,
- LibFillResults.FillResults memory feeOrderFillResults
- );
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol
deleted file mode 100644
index 4a81abf76..000000000
--- a/contracts/core/contracts/extensions/Forwarder/libs/LibConstants.sol
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
-import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol";
-import "@0x/contracts-tokens/contracts/tokens/EtherToken/IEtherToken.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol";
-
-
-contract LibConstants {
-
- using LibBytes for bytes;
-
- bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
- bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
- uint256 constant internal MAX_UINT = 2**256 - 1;
- uint256 constant internal PERCENTAGE_DENOMINATOR = 10**18;
- uint256 constant internal MAX_FEE_PERCENTAGE = 5 * PERCENTAGE_DENOMINATOR / 100; // 5%
- uint256 constant internal MAX_WETH_FILL_PERCENTAGE = 95 * PERCENTAGE_DENOMINATOR / 100; // 95%
-
- // solhint-disable var-name-mixedcase
- IExchange internal EXCHANGE;
- IEtherToken internal ETHER_TOKEN;
- IERC20Token internal ZRX_TOKEN;
- bytes internal ZRX_ASSET_DATA;
- bytes internal WETH_ASSET_DATA;
- // solhint-enable var-name-mixedcase
-
- constructor (
- address _exchange,
- bytes memory _zrxAssetData,
- bytes memory _wethAssetData
- )
- public
- {
- EXCHANGE = IExchange(_exchange);
- ZRX_ASSET_DATA = _zrxAssetData;
- WETH_ASSET_DATA = _wethAssetData;
-
- address etherToken = _wethAssetData.readAddress(16);
- address zrxToken = _zrxAssetData.readAddress(16);
- ETHER_TOKEN = IEtherToken(etherToken);
- ZRX_TOKEN = IERC20Token(zrxToken);
- }
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol b/contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol
deleted file mode 100644
index fb3ade1db..000000000
--- a/contracts/core/contracts/extensions/Forwarder/libs/LibForwarderErrors.sol
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-// solhint-disable
-pragma solidity 0.4.24;
-
-
-/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.
-contract LibForwarderErrors {
- string constant FEE_PERCENTAGE_TOO_LARGE = "FEE_PROPORTION_TOO_LARGE"; // Provided fee percentage greater than 5%.
- string constant INSUFFICIENT_ETH_REMAINING = "INSUFFICIENT_ETH_REMAINING"; // Not enough ETH remaining to pay feeRecipient.
- string constant OVERSOLD_WETH = "OVERSOLD_WETH"; // More WETH sold than provided with current message call.
- string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired purchase amount not completely filled (required for ZRX fees only).
- string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Asset transfer failed.
- string constant UNSUPPORTED_ASSET_PROXY = "UNSUPPORTED_ASSET_PROXY"; // Proxy in assetData not supported.
- string constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY = "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"; // Fallback function may only be used for WETH withdrawals.
- string constant INVALID_MSG_VALUE = "INVALID_MSG_VALUE"; // msg.value must be greater than 0.
- string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Amount must equal 1.
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol
deleted file mode 100644
index 9e7f80d97..000000000
--- a/contracts/core/contracts/extensions/Forwarder/mixins/MAssets.sol
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "../interfaces/IAssets.sol";
-
-
-contract MAssets is
- IAssets
-{
- /// @dev Transfers given amount of asset to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferAssetToSender(
- bytes memory assetData,
- uint256 amount
- )
- internal;
-
- /// @dev Decodes ERC20 assetData and transfers given amount to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferERC20Token(
- bytes memory assetData,
- uint256 amount
- )
- internal;
-
- /// @dev Decodes ERC721 assetData and transfers given amount to sender.
- /// @param assetData Byte array encoded for the respective asset proxy.
- /// @param amount Amount of asset to transfer to sender.
- function transferERC721Token(
- bytes memory assetData,
- uint256 amount
- )
- internal;
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol
deleted file mode 100644
index d9e71786a..000000000
--- a/contracts/core/contracts/extensions/Forwarder/mixins/MExchangeWrapper.sol
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-libs/contracts/libs/LibFillResults.sol";
-
-
-contract MExchangeWrapper {
-
- /// @dev Fills the input order.
- /// Returns false if the transaction would otherwise revert.
- /// @param order Order struct containing order specifications.
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
- /// @param signature Proof that order has been created by maker.
- /// @return Amounts filled and fees paid by maker and taker.
- function fillOrderNoThrow(
- LibOrder.Order memory order,
- uint256 takerAssetFillAmount,
- bytes memory signature
- )
- internal
- returns (LibFillResults.FillResults memory fillResults);
-
- /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.
- /// Returns false if the transaction would otherwise revert.
- /// @param orders Array of order specifications.
- /// @param wethSellAmount Desired amount of WETH to sell.
- /// @param signatures Proofs that orders have been signed by makers.
- /// @return Amounts filled and fees paid by makers and taker.
- function marketSellWeth(
- LibOrder.Order[] memory orders,
- uint256 wethSellAmount,
- bytes[] memory signatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.
- /// Returns false if the transaction would otherwise revert.
- /// The asset being sold by taker must always be WETH.
- /// @param orders Array of order specifications.
- /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
- /// @param signatures Proofs that orders have been signed by makers.
- /// @return Amounts filled and fees paid by makers and taker.
- function marketBuyExactAmountWithWeth(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount,
- bytes[] memory signatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee
- /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).
- /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX
- /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
- /// The asset being sold by taker must always be WETH.
- /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.
- /// @param zrxBuyAmount Desired amount of ZRX to buy.
- /// @param signatures Proofs that orders have been created by makers.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function marketBuyExactZrxWithWeth(
- LibOrder.Order[] memory orders,
- uint256 zrxBuyAmount,
- bytes[] memory signatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-}
diff --git a/contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol b/contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol
deleted file mode 100644
index 88e77be4e..000000000
--- a/contracts/core/contracts/extensions/Forwarder/mixins/MWeth.sol
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-
-contract MWeth {
-
- /// @dev Converts message call's ETH value into WETH.
- function convertEthToWeth()
- internal;
-
- /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
- /// Refunds any excess ETH to msg.sender.
- /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.
- /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.
- /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- /// @param feeRecipient Address that will receive ETH when orders are filled.
- function transferEthFeeAndRefund(
- uint256 wethSoldExcludingFeeOrders,
- uint256 wethSoldForZrx,
- uint256 feePercentage,
- address feeRecipient
- )
- internal;
-}
diff --git a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol b/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol
deleted file mode 100644
index 33dd1326c..000000000
--- a/contracts/core/contracts/extensions/OrderValidator/OrderValidator.sol
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol";
-import "@0x/contracts-libs/contracts/libs/LibOrder.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol";
-import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol";
-import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol";
-
-
-contract OrderValidator {
-
- using LibBytes for bytes;
-
- bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
- bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
-
- struct TraderInfo {
- uint256 makerBalance; // Maker's balance of makerAsset
- uint256 makerAllowance; // Maker's allowance to corresponding AssetProxy
- uint256 takerBalance; // Taker's balance of takerAsset
- uint256 takerAllowance; // Taker's allowance to corresponding AssetProxy
- uint256 makerZrxBalance; // Maker's balance of ZRX
- uint256 makerZrxAllowance; // Maker's allowance of ZRX to ERC20Proxy
- uint256 takerZrxBalance; // Taker's balance of ZRX
- uint256 takerZrxAllowance; // Taker's allowance of ZRX to ERC20Proxy
- }
-
- // solhint-disable var-name-mixedcase
- IExchange internal EXCHANGE;
- bytes internal ZRX_ASSET_DATA;
- // solhint-enable var-name-mixedcase
-
- constructor (address _exchange, bytes memory _zrxAssetData)
- public
- {
- EXCHANGE = IExchange(_exchange);
- ZRX_ASSET_DATA = _zrxAssetData;
- }
-
- /// @dev Fetches information for order and maker/taker of order.
- /// @param order The order structure.
- /// @param takerAddress Address that will be filling the order.
- /// @return OrderInfo and TraderInfo instances for given order.
- function getOrderAndTraderInfo(LibOrder.Order memory order, address takerAddress)
- public
- view
- returns (LibOrder.OrderInfo memory orderInfo, TraderInfo memory traderInfo)
- {
- orderInfo = EXCHANGE.getOrderInfo(order);
- traderInfo = getTraderInfo(order, takerAddress);
- return (orderInfo, traderInfo);
- }
-
- /// @dev Fetches information for all passed in orders and the makers/takers of each order.
- /// @param orders Array of order specifications.
- /// @param takerAddresses Array of taker addresses corresponding to each order.
- /// @return Arrays of OrderInfo and TraderInfo instances that correspond to each order.
- function getOrdersAndTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)
- public
- view
- returns (LibOrder.OrderInfo[] memory ordersInfo, TraderInfo[] memory tradersInfo)
- {
- ordersInfo = EXCHANGE.getOrdersInfo(orders);
- tradersInfo = getTradersInfo(orders, takerAddresses);
- return (ordersInfo, tradersInfo);
- }
-
- /// @dev Fetches balance and allowances for maker and taker of order.
- /// @param order The order structure.
- /// @param takerAddress Address that will be filling the order.
- /// @return Balances and allowances of maker and taker of order.
- function getTraderInfo(LibOrder.Order memory order, address takerAddress)
- public
- view
- returns (TraderInfo memory traderInfo)
- {
- (traderInfo.makerBalance, traderInfo.makerAllowance) = getBalanceAndAllowance(order.makerAddress, order.makerAssetData);
- (traderInfo.takerBalance, traderInfo.takerAllowance) = getBalanceAndAllowance(takerAddress, order.takerAssetData);
- bytes memory zrxAssetData = ZRX_ASSET_DATA;
- (traderInfo.makerZrxBalance, traderInfo.makerZrxAllowance) = getBalanceAndAllowance(order.makerAddress, zrxAssetData);
- (traderInfo.takerZrxBalance, traderInfo.takerZrxAllowance) = getBalanceAndAllowance(takerAddress, zrxAssetData);
- return traderInfo;
- }
-
- /// @dev Fetches balances and allowances of maker and taker for each provided order.
- /// @param orders Array of order specifications.
- /// @param takerAddresses Array of taker addresses corresponding to each order.
- /// @return Array of balances and allowances for maker and taker of each order.
- function getTradersInfo(LibOrder.Order[] memory orders, address[] memory takerAddresses)
- public
- view
- returns (TraderInfo[] memory)
- {
- uint256 ordersLength = orders.length;
- TraderInfo[] memory tradersInfo = new TraderInfo[](ordersLength);
- for (uint256 i = 0; i != ordersLength; i++) {
- tradersInfo[i] = getTraderInfo(orders[i], takerAddresses[i]);
- }
- return tradersInfo;
- }
-
- /// @dev Fetches token balances and allowances of an address to given assetProxy. Supports ERC20 and ERC721.
- /// @param target Address to fetch balances and allowances of.
- /// @param assetData Encoded data that can be decoded by a specified proxy contract when transferring asset.
- /// @return Balance of asset and allowance set to given proxy of asset.
- /// For ERC721 tokens, these values will always be 1 or 0.
- function getBalanceAndAllowance(address target, bytes memory assetData)
- public
- view
- returns (uint256 balance, uint256 allowance)
- {
- bytes4 assetProxyId = assetData.readBytes4(0);
- address token = assetData.readAddress(16);
- address assetProxy = EXCHANGE.getAssetProxy(assetProxyId);
-
- if (assetProxyId == ERC20_DATA_ID) {
- // Query balance
- balance = IERC20Token(token).balanceOf(target);
-
- // Query allowance
- allowance = IERC20Token(token).allowance(target, assetProxy);
- } else if (assetProxyId == ERC721_DATA_ID) {
- uint256 tokenId = assetData.readUint256(36);
-
- // Query owner of tokenId
- address owner = getERC721TokenOwner(token, tokenId);
-
- // Set balance to 1 if tokenId is owned by target
- balance = target == owner ? 1 : 0;
-
- // Check if ERC721Proxy is approved to spend tokenId
- bool isApproved = IERC721Token(token).isApprovedForAll(target, assetProxy);
-
- // Set alowance to 1 if ERC721Proxy is approved to spend tokenId
- allowance = isApproved ? 1 : 0;
- } else {
- revert("UNSUPPORTED_ASSET_PROXY");
- }
- return (balance, allowance);
- }
-
- /// @dev Fetches token balances and allowances of an address for each given assetProxy. Supports ERC20 and ERC721.
- /// @param target Address to fetch balances and allowances of.
- /// @param assetData Array of encoded byte arrays that can be decoded by a specified proxy contract when transferring asset.
- /// @return Balances and allowances of assets.
- /// For ERC721 tokens, these values will always be 1 or 0.
- function getBalancesAndAllowances(address target, bytes[] memory assetData)
- public
- view
- returns (uint256[] memory, uint256[] memory)
- {
- uint256 length = assetData.length;
- uint256[] memory balances = new uint256[](length);
- uint256[] memory allowances = new uint256[](length);
- for (uint256 i = 0; i != length; i++) {
- (balances[i], allowances[i]) = getBalanceAndAllowance(target, assetData[i]);
- }
- return (balances, allowances);
- }
-
- /// @dev Calls `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.
- /// @param token Address of ERC721 token.
- /// @param tokenId The identifier for the specific NFT.
- /// @return Owner of tokenId or null address if unowned.
- function getERC721TokenOwner(address token, uint256 tokenId)
- public
- view
- returns (address owner)
- {
- assembly {
- // load free memory pointer
- let cdStart := mload(64)
-
- // bytes4(keccak256(ownerOf(uint256))) = 0x6352211e
- mstore(cdStart, 0x6352211e00000000000000000000000000000000000000000000000000000000)
- mstore(add(cdStart, 4), tokenId)
-
- // staticcall `ownerOf(tokenId)`
- // `ownerOf` will revert if tokenId is not owned
- let success := staticcall(
- gas, // forward all gas
- token, // call token contract
- cdStart, // start of calldata
- 36, // length of input is 36 bytes
- cdStart, // write output over input
- 32 // size of output is 32 bytes
- )
-
- // Success implies that tokenId is owned
- // Copy owner from return data if successful
- if success {
- owner := mload(cdStart)
- }
- }
-
- // Owner initialized to address(0), no need to modify if call is unsuccessful
- return owner;
- }
-}
diff --git a/contracts/core/package.json b/contracts/core/package.json
index 62547c311..b7077f4b4 100644
--- a/contracts/core/package.json
+++ b/contracts/core/package.json
@@ -33,7 +33,7 @@
"lint-contracts": "solhint contracts/**/**/**/**/*.sol"
},
"config": {
- "abis": "generated-artifacts/@(AssetProxyOwner|DutchAuction|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|MixinAuthorizable|MultiAssetProxy|OrderValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestStaticCallReceiver).json"
+ "abis": "generated-artifacts/@(AssetProxyOwner|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiAssetProxy|TestSignatureValidator|TestAssetProxyOwner|TestAssetProxyDispatcher|TestExchangeInternals|TestStaticCallReceiver).json"
},
"repository": {
"type": "git",
diff --git a/contracts/core/src/artifacts/index.ts b/contracts/core/src/artifacts/index.ts
index 7b598a6d9..c5d12f10b 100644
--- a/contracts/core/src/artifacts/index.ts
+++ b/contracts/core/src/artifacts/index.ts
@@ -1,14 +1,11 @@
import { ContractArtifact } from 'ethereum-types';
import * as AssetProxyOwner from '../../generated-artifacts/AssetProxyOwner.json';
-import * as DutchAuction from '../../generated-artifacts/DutchAuction.json';
import * as ERC20Proxy from '../../generated-artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json';
import * as Exchange from '../../generated-artifacts/Exchange.json';
-import * as Forwarder from '../../generated-artifacts/Forwarder.json';
import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json';
import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json';
-import * as OrderValidator from '../../generated-artifacts/OrderValidator.json';
import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json';
import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json';
import * as TestExchangeInternals from '../../generated-artifacts/TestExchangeInternals.json';
@@ -17,14 +14,11 @@ import * as TestStaticCallReceiver from '../../generated-artifacts/TestStaticCal
export const artifacts = {
AssetProxyOwner: AssetProxyOwner as ContractArtifact,
- DutchAuction: DutchAuction as ContractArtifact,
ERC20Proxy: ERC20Proxy as ContractArtifact,
ERC721Proxy: ERC721Proxy as ContractArtifact,
Exchange: Exchange as ContractArtifact,
- Forwarder: Forwarder as ContractArtifact,
MixinAuthorizable: MixinAuthorizable as ContractArtifact,
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
- OrderValidator: OrderValidator as ContractArtifact,
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact,
TestExchangeInternals: TestExchangeInternals as ContractArtifact,
diff --git a/contracts/core/src/index.ts b/contracts/core/src/index.ts
index d55f08ea2..ba813e7ca 100644
--- a/contracts/core/src/index.ts
+++ b/contracts/core/src/index.ts
@@ -1,2 +1,3 @@
export * from './artifacts';
export * from './wrappers';
+export * from '../test/utils';
diff --git a/contracts/core/src/wrappers/index.ts b/contracts/core/src/wrappers/index.ts
index ce3714ed7..01b121054 100644
--- a/contracts/core/src/wrappers/index.ts
+++ b/contracts/core/src/wrappers/index.ts
@@ -1,11 +1,8 @@
export * from '../../generated-wrappers/asset_proxy_owner';
-export * from '../../generated-wrappers/dutch_auction';
export * from '../../generated-wrappers/erc20_proxy';
export * from '../../generated-wrappers/erc721_proxy';
export * from '../../generated-wrappers/exchange';
-export * from '../../generated-wrappers/forwarder';
export * from '../../generated-wrappers/mixin_authorizable';
-export * from '../../generated-wrappers/order_validator';
export * from '../../generated-wrappers/test_asset_proxy_dispatcher';
export * from '../../generated-wrappers/test_asset_proxy_owner';
export * from '../../generated-wrappers/test_exchange_internals';
diff --git a/contracts/core/test/exchange/signature_validator.ts b/contracts/core/test/exchange/signature_validator.ts
index ae372d677..9313fca6c 100644
--- a/contracts/core/test/exchange/signature_validator.ts
+++ b/contracts/core/test/exchange/signature_validator.ts
@@ -1,3 +1,5 @@
+import { ValidatorContract, WalletContract } from '@0x/contracts-examples';
+import { artifacts as examplesArtifacts } from '@0x/contracts-examples';
import {
addressUtils,
chaiSetup,
@@ -22,8 +24,6 @@ import {
TestSignatureValidatorContract,
TestSignatureValidatorSignatureValidatorApprovalEventArgs,
TestStaticCallReceiverContract,
- ValidatorContract,
- WalletContract,
} from '../../src';
chaiSetup.configure();
@@ -62,13 +62,13 @@ describe('MixinSignatureValidator', () => {
txDefaults,
);
testWallet = await WalletContract.deployFrom0xArtifactAsync(
- artifacts.Wallet,
+ examplesArtifacts.Wallet,
provider,
txDefaults,
signerAddress,
);
testValidator = await ValidatorContract.deployFrom0xArtifactAsync(
- artifacts.Validator,
+ examplesArtifacts.Validator,
provider,
txDefaults,
signerAddress,
diff --git a/contracts/core/test/exchange/transactions.ts b/contracts/core/test/exchange/transactions.ts
index ad0ca41c2..746f3cb04 100644
--- a/contracts/core/test/exchange/transactions.ts
+++ b/contracts/core/test/exchange/transactions.ts
@@ -1,3 +1,4 @@
+import { artifacts as examplesArtifacts, ExchangeWrapperContract, WhitelistContract } from '@0x/contracts-examples';
import {
chaiSetup,
constants,
@@ -21,8 +22,6 @@ import * as _ from 'lodash';
import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
import { ExchangeContract } from '../../generated-wrappers/exchange';
-import { ExchangeWrapperContract } from '../../generated-wrappers/exchange_wrapper';
-import { WhitelistContract } from '../../generated-wrappers/whitelist';
import { artifacts } from '../../src/artifacts';
import { ERC20Wrapper } from '../utils/erc20_wrapper';
import { ExchangeWrapper } from '../utils/exchange_wrapper';
@@ -222,7 +221,7 @@ describe('Exchange transactions', () => {
before(async () => {
exchangeWrapperContract = await ExchangeWrapperContract.deployFrom0xArtifactAsync(
- artifacts.ExchangeWrapper,
+ examplesArtifacts.ExchangeWrapper,
provider,
txDefaults,
exchange.address,
@@ -336,7 +335,7 @@ describe('Exchange transactions', () => {
before(async () => {
whitelist = await WhitelistContract.deployFrom0xArtifactAsync(
- artifacts.Whitelist,
+ examplesArtifacts.Whitelist,
provider,
txDefaults,
exchange.address,
diff --git a/contracts/core/test/extensions/dutch_auction.ts b/contracts/core/test/extensions/dutch_auction.ts
deleted file mode 100644
index b396d4206..000000000
--- a/contracts/core/test/extensions/dutch_auction.ts
+++ /dev/null
@@ -1,455 +0,0 @@
-import {
- chaiSetup,
- constants,
- ContractName,
- ERC20BalancesByOwner,
- expectTransactionFailedAsync,
- getLatestBlockTimestampAsync,
- OrderFactory,
- provider,
- txDefaults,
- web3Wrapper,
-} from '@0x/contracts-test-utils';
-import {
- artifacts as tokensArtifacts,
- DummyERC20TokenContract,
- DummyERC721TokenContract,
- WETH9Contract,
-} from '@0x/contracts-tokens';
-import { BlockchainLifecycle } from '@0x/dev-utils';
-import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
-import { RevertReason, SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import * as chai from 'chai';
-import ethAbi = require('ethereumjs-abi');
-import * as ethUtil from 'ethereumjs-util';
-import * as _ from 'lodash';
-
-import { DutchAuctionContract } from '../../generated-wrappers/dutch_auction';
-import { ExchangeContract } from '../../generated-wrappers/exchange';
-import { artifacts } from '../../src/artifacts';
-import { ERC20Wrapper } from '../utils/erc20_wrapper';
-import { ERC721Wrapper } from '../utils/erc721_wrapper';
-import { ExchangeWrapper } from '../utils/exchange_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-const DECIMALS_DEFAULT = 18;
-
-describe(ContractName.DutchAuction, () => {
- 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;
-
- function extendMakerAssetData(makerAssetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string {
- return ethUtil.bufferToHex(
- Buffer.concat([
- ethUtil.toBuffer(makerAssetData),
- ethUtil.toBuffer(
- (ethAbi as any).rawEncode(
- ['uint256', 'uint256'],
- [beginTimeSeconds.toString(), beginAmount.toString()],
- ),
- ),
- ]),
- );
- }
-
- before(async () => {
- await blockchainLifecycle.startAsync();
- 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(tokensArtifacts.WETH9, provider, txDefaults);
- erc20Wrapper.addDummyTokenContract(wethContract as any);
-
- const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
- artifacts.Exchange,
- provider,
- txDefaults,
- zrxAssetData,
- );
- const exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
- await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
- await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
-
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
- from: owner,
- });
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
- from: owner,
- });
-
- const dutchAuctionInstance = await DutchAuctionContract.deployFrom0xArtifactAsync(
- artifacts.DutchAuction,
- provider,
- txDefaults,
- exchangeInstance.address,
- );
- dutchAuctionContract = new DutchAuctionContract(
- dutchAuctionInstance.abi,
- dutchAuctionInstance.address,
- 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.sendTransactionAsync(
- erc20Proxy.address,
- constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
- { 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(),
- exchangeAddress: exchangeInstance.address,
- makerAddress,
- feeRecipientAddress,
- // taker address or sender address should be set to the ducth auction contract
- takerAddress: dutchAuctionContract.address,
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
- makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
- takerAssetAmount: auctionEndAmount,
- expirationTimeSeconds: auctionEndTimeSeconds,
- makerFee: constants.ZERO_AMOUNT,
- takerFee: constants.ZERO_AMOUNT,
- };
- // 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);
- });
- 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);
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- });
- const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
- 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);
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- expirationTimeSeconds: auctionEndTimeSeconds,
- });
- const auctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
- 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 dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
- buyOrder = await buyerOrderFactory.newSignedOrderAsync({
- makerAssetAmount: beforeAuctionDetails.currentAmount.times(2),
- });
- await web3Wrapper.awaitTransactionSuccessAsync(
- await dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- );
- const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(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),
- });
- const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(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),
- });
- const txHash = await dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- );
- await web3Wrapper.awaitTransactionSuccessAsync(txHash);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(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);
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- expirationTimeSeconds: auctionEndTimeSeconds,
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- });
- return expectTransactionFailedAsync(
- dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- RevertReason.AuctionExpired,
- );
- });
- it('cannot be filled for less than the current price', async () => {
- buyOrder = await buyerOrderFactory.newSignedOrderAsync({
- makerAssetAmount: sellOrder.takerAssetAmount,
- });
- return expectTransactionFailedAsync(
- dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- RevertReason.AuctionInvalidAmount,
- );
- });
- it('auction begin amount must be higher than final amount ', async () => {
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- takerAssetAmount: auctionBeginAmount.plus(1),
- });
- return expectTransactionFailedAsync(
- dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- RevertReason.AuctionInvalidAmount,
- );
- });
- it('begin time is less than end time', async () => {
- auctionBeginTimeSeconds = new BigNumber(auctionEndTimeSeconds).plus(tenMinutesInSeconds);
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- expirationTimeSeconds: auctionEndTimeSeconds,
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- });
- return expectTransactionFailedAsync(
- dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- RevertReason.AuctionInvalidBeginTime,
- );
- });
- it('asset data contains auction parameters', async () => {
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- });
- return expectTransactionFailedAsync(
- dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- RevertReason.InvalidAssetData,
- );
- });
- describe('ERC721', () => {
- it('should match orders when ERC721', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- sellOrder = await sellerOrderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: extendMakerAssetData(
- assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- auctionBeginTimeSeconds,
- auctionBeginAmount,
- ),
- });
- buyOrder = await buyerOrderFactory.newSignedOrderAsync({
- takerAssetAmount: new BigNumber(1),
- takerAssetData: sellOrder.makerAssetData,
- });
- await web3Wrapper.awaitTransactionSuccessAsync(
- await dutchAuctionContract.matchOrders.sendTransactionAsync(
- buyOrder,
- sellOrder,
- buyOrder.signature,
- sellOrder.signature,
- {
- from: takerAddress,
- },
- ),
- );
- const afterAuctionDetails = await dutchAuctionContract.getAuctionDetails.callAsync(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.callAsync(makerAssetId);
- expect(newOwner).to.be.bignumber.equal(takerAddress);
- });
- });
- });
-});
diff --git a/contracts/core/test/extensions/forwarder.ts b/contracts/core/test/extensions/forwarder.ts
deleted file mode 100644
index ffa015dd2..000000000
--- a/contracts/core/test/extensions/forwarder.ts
+++ /dev/null
@@ -1,1288 +0,0 @@
-import {
- chaiSetup,
- constants,
- ContractName,
- ERC20BalancesByOwner,
- expectContractCreationFailedAsync,
- expectTransactionFailedAsync,
- OrderFactory,
- provider,
- sendTransactionResult,
- txDefaults,
- web3Wrapper,
-} from '@0x/contracts-test-utils';
-import {
- artifacts as tokenArtifacts,
- DummyERC20TokenContract,
- DummyERC721TokenContract,
- WETH9Contract,
-} from '@0x/contracts-tokens';
-import { BlockchainLifecycle } from '@0x/dev-utils';
-import { assetDataUtils } from '@0x/order-utils';
-import { RevertReason, SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import * as chai from 'chai';
-import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
-
-import { ExchangeContract } from '../../generated-wrappers/exchange';
-import { ForwarderContract } from '../../generated-wrappers/forwarder';
-import { artifacts } from '../../src/artifacts';
-import { ERC20Wrapper } from '../utils/erc20_wrapper';
-import { ERC721Wrapper } from '../utils/erc721_wrapper';
-import { ExchangeWrapper } from '../utils/exchange_wrapper';
-import { ForwarderWrapper } from '../utils/forwarder_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-const DECIMALS_DEFAULT = 18;
-const MAX_WETH_FILL_PERCENTAGE = 95;
-
-describe(ContractName.Forwarder, () => {
- let makerAddress: string;
- let owner: string;
- let takerAddress: string;
- let feeRecipientAddress: string;
- let otherAddress: string;
- let defaultMakerAssetAddress: string;
- let zrxAssetData: string;
- let wethAssetData: string;
-
- let weth: DummyERC20TokenContract;
- let zrxToken: DummyERC20TokenContract;
- let erc20TokenA: DummyERC20TokenContract;
- let erc721Token: DummyERC721TokenContract;
- let forwarderContract: ForwarderContract;
- let wethContract: WETH9Contract;
- let forwarderWrapper: ForwarderWrapper;
- let exchangeWrapper: ExchangeWrapper;
-
- let orderWithoutFee: SignedOrder;
- let orderWithFee: SignedOrder;
- let feeOrder: SignedOrder;
- let orderFactory: OrderFactory;
- let erc20Wrapper: ERC20Wrapper;
- let erc20Balances: ERC20BalancesByOwner;
- let tx: TransactionReceiptWithDecodedLogs;
-
- let erc721MakerAssetIds: BigNumber[];
- let takerEthBalanceBefore: BigNumber;
- let feePercentage: BigNumber;
- let gasPrice: BigNumber;
-
- before(async () => {
- await blockchainLifecycle.startAsync();
- const accounts = await web3Wrapper.getAvailableAddressesAsync();
- const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress, otherAddress] = accounts);
-
- const txHash = await web3Wrapper.sendTransactionAsync({ from: accounts[0], to: accounts[0], value: 0 });
- const transaction = await web3Wrapper.getTransactionByHashAsync(txHash);
- gasPrice = new BigNumber(transaction.gasPrice);
-
- const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
- erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
-
- const numDummyErc20ToDeploy = 3;
- [erc20TokenA, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
- numDummyErc20ToDeploy,
- constants.DUMMY_TOKEN_DECIMALS,
- );
- const erc20Proxy = await erc20Wrapper.deployProxyAsync();
- await erc20Wrapper.setBalancesAndAllowancesAsync();
-
- [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(tokenArtifacts.WETH9, provider, txDefaults);
- weth = new DummyERC20TokenContract(wethContract.abi, wethContract.address, provider);
- erc20Wrapper.addDummyTokenContract(weth);
-
- wethAssetData = assetDataUtils.encodeERC20AssetData(wethContract.address);
- zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
- artifacts.Exchange,
- provider,
- txDefaults,
- zrxAssetData,
- );
- exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider);
- await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
- await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
-
- await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
- from: owner,
- });
- await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
- from: owner,
- });
-
- defaultMakerAssetAddress = erc20TokenA.address;
- const defaultTakerAssetAddress = wethContract.address;
- const defaultOrderParams = {
- exchangeAddress: exchangeInstance.address,
- makerAddress,
- feeRecipientAddress,
- makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
- takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
- makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), DECIMALS_DEFAULT),
- takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), DECIMALS_DEFAULT),
- makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), DECIMALS_DEFAULT),
- };
- const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
- orderFactory = new OrderFactory(privateKey, defaultOrderParams);
-
- const forwarderInstance = await ForwarderContract.deployFrom0xArtifactAsync(
- artifacts.Forwarder,
- provider,
- txDefaults,
- exchangeInstance.address,
- zrxAssetData,
- wethAssetData,
- );
- forwarderContract = new ForwarderContract(forwarderInstance.abi, forwarderInstance.address, provider);
- forwarderWrapper = new ForwarderWrapper(forwarderContract, provider);
- const zrxDepositAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.transfer.sendTransactionAsync(forwarderContract.address, zrxDepositAmount),
- );
- erc20Wrapper.addTokenOwnerAddress(forwarderInstance.address);
- });
- after(async () => {
- await blockchainLifecycle.revertAsync();
- });
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- erc20Balances = await erc20Wrapper.getBalancesAsync();
- takerEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- orderWithoutFee = await orderFactory.newSignedOrderAsync();
- feeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- orderWithFee = await orderFactory.newSignedOrderAsync({
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
-
- describe('constructor', () => {
- it('should revert if assetProxy is unregistered', async () => {
- const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
- artifacts.Exchange,
- provider,
- txDefaults,
- zrxAssetData,
- );
- return expectContractCreationFailedAsync(
- (ForwarderContract.deployFrom0xArtifactAsync(
- artifacts.Forwarder,
- provider,
- txDefaults,
- exchangeInstance.address,
- zrxAssetData,
- wethAssetData,
- ) as any) as sendTransactionResult,
- RevertReason.UnregisteredAssetProxy,
- );
- });
- });
- describe('marketSellOrdersWithEth without extra fees', () => {
- it('should fill a single order', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2);
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue(
- ethValue,
- MAX_WETH_FILL_PERCENTAGE,
- );
- const makerAssetFillAmount = primaryTakerAssetFillAmount
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fill multiple orders', async () => {
- const secondOrderWithoutFee = await orderFactory.newSignedOrderAsync();
- const ordersWithoutFee = [orderWithoutFee, secondOrderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const ethValue = ordersWithoutFee[0].takerAssetAmount.plus(
- ordersWithoutFee[1].takerAssetAmount.dividedToIntegerBy(2),
- );
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue(
- ethValue,
- MAX_WETH_FILL_PERCENTAGE,
- );
- const firstTakerAssetFillAmount = ordersWithoutFee[0].takerAssetAmount;
- const secondTakerAssetFillAmount = primaryTakerAssetFillAmount.minus(firstTakerAssetFillAmount);
-
- const makerAssetFillAmount = ordersWithoutFee[0].makerAssetAmount.plus(
- ordersWithoutFee[1].makerAssetAmount
- .times(secondTakerAssetFillAmount)
- .dividedToIntegerBy(ordersWithoutFee[1].takerAssetAmount),
- );
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fill the order and pay ZRX fees from a single feeOrder', async () => {
- const ordersWithFee = [orderWithFee];
- const feeOrders = [feeOrder];
- const ethValue = orderWithFee.takerAssetAmount.dividedToIntegerBy(2);
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue(
- ethValue,
- MAX_WETH_FILL_PERCENTAGE,
- );
- const makerAssetFillAmount = primaryTakerAssetFillAmount
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- const feeAmount = ForwarderWrapper.getPercentageOfValue(
- orderWithFee.takerFee.dividedToIntegerBy(2),
- MAX_WETH_FILL_PERCENTAGE,
- );
- const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders);
- const totalEthSpent = primaryTakerAssetFillAmount
- .plus(wethSpentOnFeeOrders)
- .plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fill the orders and pay ZRX from multiple feeOrders', async () => {
- const ordersWithFee = [orderWithFee];
- const ethValue = orderWithFee.takerAssetAmount;
- const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- const makerAssetAmount = orderWithFee.takerFee.dividedToIntegerBy(2);
- const takerAssetAmount = feeOrder.takerAssetAmount
- .times(makerAssetAmount)
- .dividedToIntegerBy(feeOrder.makerAssetAmount);
-
- const firstFeeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData,
- makerAssetAmount,
- takerAssetAmount,
- });
- const secondFeeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData,
- makerAssetAmount,
- takerAssetAmount,
- });
- const feeOrders = [firstFeeOrder, secondFeeOrder];
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue(
- ethValue,
- MAX_WETH_FILL_PERCENTAGE,
- );
- const makerAssetFillAmount = primaryTakerAssetFillAmount
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- const feeAmount = ForwarderWrapper.getPercentageOfValue(orderWithFee.takerFee, MAX_WETH_FILL_PERCENTAGE);
- const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders);
- const totalEthSpent = primaryTakerAssetFillAmount
- .plus(wethSpentOnFeeOrders)
- .plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fill the order when token is ZRX with fees', async () => {
- orderWithFee = await orderFactory.newSignedOrderAsync({
- makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- const ordersWithFee = [orderWithFee];
- const feeOrders: SignedOrder[] = [];
- const ethValue = orderWithFee.takerAssetAmount.dividedToIntegerBy(2);
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2);
- const totalEthSpent = ethValue.plus(gasPrice.times(tx.gasUsed));
- const takerFeePaid = orderWithFee.takerFee.dividedToIntegerBy(2);
- const makerFeePaid = orderWithFee.makerFee.dividedToIntegerBy(2);
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFillAmount).minus(makerFeePaid),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFillAmount).minus(takerFeePaid),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(ethValue),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[forwarderContract.address][zrxToken.address],
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const ethValue = orderWithoutFee.takerAssetAmount.times(2);
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const totalEthSpent = orderWithoutFee.takerAssetAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- });
- it('should revert if ZRX cannot be fully repurchased', async () => {
- orderWithFee = await orderFactory.newSignedOrderAsync({
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(50), DECIMALS_DEFAULT),
- });
- const ordersWithFee = [orderWithFee];
- feeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),
- makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- const feeOrders = [feeOrder];
- const ethValue = orderWithFee.takerAssetAmount;
- return expectTransactionFailedAsync(
- forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- }),
- RevertReason.CompleteFillFailed,
- );
- });
- it('should not fill orders with different makerAssetData than the first order', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- const erc721SignedOrder = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- });
- const erc20SignedOrder = await orderFactory.newSignedOrderAsync();
- const ordersWithoutFee = [erc20SignedOrder, erc721SignedOrder];
- const feeOrders: SignedOrder[] = [];
- const ethValue = erc20SignedOrder.takerAssetAmount.plus(erc721SignedOrder.takerAssetAmount);
-
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(ordersWithoutFee, feeOrders, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const totalEthSpent = erc20SignedOrder.takerAssetAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- });
- });
- describe('marketSellOrdersWithEth with extra fees', () => {
- it('should fill the order and send fee to feeRecipient', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const ethValue = orderWithoutFee.takerAssetAmount.div(2);
-
- const baseFeePercentage = 2;
- feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage);
- const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
- tx = await forwarderWrapper.marketSellOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- {
- value: ethValue,
- from: takerAddress,
- },
- { feePercentage, feeRecipient: feeRecipientAddress },
- );
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getPercentageOfValue(
- ethValue,
- MAX_WETH_FILL_PERCENTAGE,
- );
- const makerAssetFillAmount = primaryTakerAssetFillAmount
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- const ethSpentOnFee = ForwarderWrapper.getPercentageOfValue(primaryTakerAssetFillAmount, baseFeePercentage);
- const totalEthSpent = primaryTakerAssetFillAmount.plus(ethSpentOnFee).plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(feeRecipientEthBalanceAfter).to.be.bignumber.equal(feeRecipientEthBalanceBefore.plus(ethSpentOnFee));
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fail if the fee is set too high', async () => {
- const ethValue = orderWithoutFee.takerAssetAmount.div(2);
- const baseFeePercentage = 6;
- feePercentage = ForwarderWrapper.getPercentageOfValue(ethValue, baseFeePercentage);
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- await expectTransactionFailedAsync(
- forwarderWrapper.marketSellOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- { from: takerAddress, value: ethValue, gasPrice },
- { feePercentage, feeRecipient: feeRecipientAddress },
- ),
- RevertReason.FeePercentageTooLarge,
- );
- });
- it('should fail if there is not enough ETH remaining to pay the fee', async () => {
- const ethValue = orderWithoutFee.takerAssetAmount.div(2);
- const baseFeePercentage = 5;
- feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage);
- const ordersWithFee = [orderWithFee];
- const feeOrders = [feeOrder];
- await expectTransactionFailedAsync(
- forwarderWrapper.marketSellOrdersWithEthAsync(
- ordersWithFee,
- feeOrders,
- { from: takerAddress, value: ethValue, gasPrice },
- { feePercentage, feeRecipient: feeRecipientAddress },
- ),
- RevertReason.InsufficientEthRemaining,
- );
- });
- });
- describe('marketBuyOrdersWithEth without extra fees', () => {
- it('should buy the exact amount of makerAsset in a single order', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2);
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should buy the exact amount of makerAsset in multiple orders', async () => {
- const secondOrderWithoutFee = await orderFactory.newSignedOrderAsync();
- const ordersWithoutFee = [orderWithoutFee, secondOrderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = ordersWithoutFee[0].makerAssetAmount.plus(
- ordersWithoutFee[1].makerAssetAmount.dividedToIntegerBy(2),
- );
- const ethValue = ordersWithoutFee[0].takerAssetAmount.plus(
- ordersWithoutFee[1].takerAssetAmount.dividedToIntegerBy(2),
- );
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should buy the exact amount of makerAsset and return excess ETH', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount;
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ethValue.dividedToIntegerBy(2);
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should buy the exact amount of makerAsset and pay ZRX from feeOrders', async () => {
- const ordersWithFee = [orderWithFee];
- const feeOrders = [feeOrder];
- const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithFee.takerAssetAmount;
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = orderWithFee.takerAssetAmount.dividedToIntegerBy(2);
- const feeAmount = orderWithFee.takerFee.dividedToIntegerBy(2);
- const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders);
- const totalEthSpent = primaryTakerAssetFillAmount
- .plus(wethSpentOnFeeOrders)
- .plus(gasPrice.times(tx.gasUsed));
-
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should buy slightly greater than makerAssetAmount when buying ZRX', async () => {
- orderWithFee = await orderFactory.newSignedOrderAsync({
- makerAssetData: assetDataUtils.encodeERC20AssetData(zrxToken.address),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- const ordersWithFee = [orderWithFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithFee.takerAssetAmount;
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ForwarderWrapper.getWethForFeeOrders(
- makerAssetFillAmount,
- ordersWithFee,
- );
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- const makerAssetFilledAmount = orderWithFee.makerAssetAmount
- .times(primaryTakerAssetFillAmount)
- .dividedToIntegerBy(orderWithFee.takerAssetAmount);
- const takerFeePaid = orderWithFee.takerFee
- .times(primaryTakerAssetFillAmount)
- .dividedToIntegerBy(orderWithFee.takerAssetAmount);
- const makerFeePaid = orderWithFee.makerFee
- .times(primaryTakerAssetFillAmount)
- .dividedToIntegerBy(orderWithFee.takerAssetAmount);
- const totalZrxPurchased = makerAssetFilledAmount.minus(takerFeePaid);
- // Up to 1 wei worth of ZRX will be overbought per order
- const maxOverboughtZrx = new BigNumber(1)
- .times(orderWithFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithFee.takerAssetAmount);
-
- expect(totalZrxPurchased).to.be.bignumber.gte(makerAssetFillAmount);
- expect(totalZrxPurchased).to.be.bignumber.lte(makerAssetFillAmount.plus(maxOverboughtZrx));
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFilledAmount).minus(makerFeePaid),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].plus(totalZrxPurchased),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[forwarderContract.address][zrxToken.address],
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should revert if the amount of ETH sent is too low to fill the makerAssetAmount', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(4);
- return expectTransactionFailedAsync(
- forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- }),
- RevertReason.CompleteFillFailed,
- );
- });
- it('should buy an ERC721 asset from a single order', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = new BigNumber(1);
- const ethValue = orderWithFee.takerAssetAmount;
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- from: takerAddress,
- value: ethValue,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- expect(newOwner).to.be.bignumber.equal(takerAddress);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should revert if buying an ERC721 asset when later orders contain different makerAssetData', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- });
- const differentMakerAssetDataOrder = await orderFactory.newSignedOrderAsync();
- const ordersWithoutFee = [orderWithoutFee, differentMakerAssetDataOrder];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = new BigNumber(1).plus(differentMakerAssetDataOrder.makerAssetAmount);
- const ethValue = orderWithFee.takerAssetAmount;
- return expectTransactionFailedAsync(
- forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- }),
- RevertReason.CompleteFillFailed,
- );
- });
- it('should buy an ERC721 asset and pay ZRX fees from a single fee order', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- orderWithFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- const ordersWithFee = [orderWithFee];
- const feeOrders = [feeOrder];
- const makerAssetFillAmount = orderWithFee.makerAssetAmount;
- const primaryTakerAssetFillAmount = orderWithFee.takerAssetAmount;
- const feeAmount = orderWithFee.takerFee;
- const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders);
- const ethValue = primaryTakerAssetFillAmount.plus(wethSpentOnFeeOrders);
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const totalEthSpent = ethValue.plus(gasPrice.times(tx.gasUsed));
-
- expect(newOwner).to.be.bignumber.equal(takerAddress);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should buy an ERC721 asset and pay ZRX fees from multiple fee orders', async () => {
- const makerAssetId = erc721MakerAssetIds[0];
- orderWithFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber(1),
- makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
- takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), DECIMALS_DEFAULT),
- });
- const ordersWithFee = [orderWithFee];
- const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- const makerAssetAmount = orderWithFee.takerFee.dividedToIntegerBy(2);
- const takerAssetAmount = feeOrder.takerAssetAmount
- .times(makerAssetAmount)
- .dividedToIntegerBy(feeOrder.makerAssetAmount);
-
- const firstFeeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData,
- makerAssetAmount,
- takerAssetAmount,
- });
- const secondFeeOrder = await orderFactory.newSignedOrderAsync({
- makerAssetData,
- makerAssetAmount,
- takerAssetAmount,
- });
- const feeOrders = [firstFeeOrder, secondFeeOrder];
-
- const makerAssetFillAmount = orderWithFee.makerAssetAmount;
- const primaryTakerAssetFillAmount = orderWithFee.takerAssetAmount;
- const feeAmount = orderWithFee.takerFee;
- const wethSpentOnFeeOrders = ForwarderWrapper.getWethForFeeOrders(feeAmount, feeOrders);
- const ethValue = primaryTakerAssetFillAmount.plus(wethSpentOnFeeOrders);
-
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newOwner = await erc721Token.ownerOf.callAsync(makerAssetId);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const totalEthSpent = ethValue.plus(gasPrice.times(tx.gasUsed));
-
- expect(newOwner).to.be.bignumber.equal(takerAddress);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount).plus(wethSpentOnFeeOrders),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('Should buy slightly greater MakerAsset when exchange rate is rounded', async () => {
- // The 0x Protocol contracts round the exchange rate in favor of the Maker.
- // In this case, the taker must round up how much they're going to spend, which
- // in turn increases the amount of MakerAsset being purchased.
- // Example:
- // The taker wants to buy 5 units of the MakerAsset at a rate of 3M/2T.
- // For every 2 units of TakerAsset, the taker will receive 3 units of MakerAsset.
- // To purchase 5 units, the taker must spend 10/3 = 3.33 units of TakerAssset.
- // However, the Taker can only spend whole units.
- // Spending floor(10/3) = 3 units will yield a profit of Floor(3*3/2) = Floor(4.5) = 4 units of MakerAsset.
- // Spending ceil(10/3) = 4 units will yield a profit of Floor(4*3/2) = 6 units of MakerAsset.
- //
- // The forwarding contract will opt for the second option, which overbuys, to ensure the taker
- // receives at least the amount of MakerAsset they requested.
- //
- // Construct test case using values from example above
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber('30'),
- takerAssetAmount: new BigNumber('20'),
- makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
- takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const desiredMakerAssetFillAmount = new BigNumber('5');
- const makerAssetFillAmount = new BigNumber('6');
- const ethValue = new BigNumber('4');
- // Execute test case
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- desiredMakerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- );
- // Fetch end balances and construct expected outputs
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- // Validate test case
- expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('Should buy slightly greater MakerAsset when exchange rate is rounded, and MakerAsset is ZRX', async () => {
- // See the test case above for a detailed description of this case.
- // The difference here is that the MakerAsset is ZRX. We expect the same result as above,
- // but this tests a different code path.
- //
- // Construct test case using values from example above
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber('30'),
- takerAssetAmount: new BigNumber('20'),
- makerAssetData: zrxAssetData,
- takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const desiredMakerAssetFillAmount = new BigNumber('5');
- const makerAssetFillAmount = new BigNumber('6');
- const ethValue = new BigNumber('4');
- // Execute test case
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- desiredMakerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- );
- // Fetch end balances and construct expected outputs
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- // Validate test case
- expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('Should buy slightly greater MakerAsset when exchange rate is rounded (Regression Test)', async () => {
- // Order taken from a transaction on mainnet that failed due to a rounding error.
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber('268166666666666666666'),
- takerAssetAmount: new BigNumber('219090625878836371'),
- makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
- takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- // The taker will receive more than the desired amount of makerAsset due to rounding
- const desiredMakerAssetFillAmount = new BigNumber('5000000000000000000');
- const ethValue = new BigNumber('4084971271824171');
- const makerAssetFillAmount = ethValue
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- // Execute test case
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- desiredMakerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- );
- // Fetch end balances and construct expected outputs
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- // Validate test case
- expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('Should buy slightly greater MakerAsset when exchange rate is rounded, and MakerAsset is ZRX (Regression Test)', async () => {
- // Order taken from a transaction on mainnet that failed due to a rounding error.
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber('268166666666666666666'),
- takerAssetAmount: new BigNumber('219090625878836371'),
- makerAssetData: zrxAssetData,
- takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- // The taker will receive more than the desired amount of makerAsset due to rounding
- const desiredMakerAssetFillAmount = new BigNumber('5000000000000000000');
- const ethValue = new BigNumber('4084971271824171');
- const makerAssetFillAmount = ethValue
- .times(orderWithoutFee.makerAssetAmount)
- .dividedToIntegerBy(orderWithoutFee.takerAssetAmount);
- // Execute test case
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- desiredMakerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- );
- // Fetch end balances and construct expected outputs
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- // Validate test case
- expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('Should buy correct MakerAsset when exchange rate is NOT rounded, and MakerAsset is ZRX (Regression Test)', async () => {
- // An extra unit of TakerAsset was sent to the exchange contract to account for rounding errors, in Forwarder v1.
- // Specifically, the takerFillAmount was calculated using Floor(desiredMakerAmount * exchangeRate) + 1
- // We have since changed this to be Ceil(desiredMakerAmount * exchangeRate)
- // These calculations produce different results when `desiredMakerAmount * exchangeRate` is an integer.
- //
- // This test verifies that `ceil` is sufficient:
- // Let TakerAssetAmount = MakerAssetAmount * 2
- // -> exchangeRate = TakerAssetAmount / MakerAssetAmount = (2*MakerAssetAmount)/MakerAssetAmount = 2
- // .: desiredMakerAmount * exchangeRate is an integer.
- //
- // Construct test case using values from example above
- orderWithoutFee = await orderFactory.newSignedOrderAsync({
- makerAssetAmount: new BigNumber('30'),
- takerAssetAmount: new BigNumber('60'),
- makerAssetData: zrxAssetData,
- takerAssetData: assetDataUtils.encodeERC20AssetData(weth.address),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- });
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = new BigNumber('5');
- const ethValue = new BigNumber('10');
- // Execute test case
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(ordersWithoutFee, feeOrders, makerAssetFillAmount, {
- value: ethValue,
- from: takerAddress,
- });
- // Fetch end balances and construct expected outputs
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const newBalances = await erc20Wrapper.getBalancesAsync();
- const primaryTakerAssetFillAmount = ethValue;
- const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
- // Validate test case
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][zrxToken.address].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[takerAddress][zrxToken.address].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- });
- describe('marketBuyOrdersWithEth with extra fees', () => {
- it('should buy an asset and send fee to feeRecipient', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount;
-
- const baseFeePercentage = 2;
- feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage);
- const feeRecipientEthBalanceBefore = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
- tx = await forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- makerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- { feePercentage, feeRecipient: feeRecipientAddress },
- );
- const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(takerAddress);
- const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
- const feeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(feeRecipientAddress);
- const newBalances = await erc20Wrapper.getBalancesAsync();
-
- const primaryTakerAssetFillAmount = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2);
- const ethSpentOnFee = ForwarderWrapper.getPercentageOfValue(primaryTakerAssetFillAmount, baseFeePercentage);
- const totalEthSpent = primaryTakerAssetFillAmount.plus(ethSpentOnFee).plus(gasPrice.times(tx.gasUsed));
-
- expect(feeRecipientEthBalanceAfter).to.be.bignumber.equal(feeRecipientEthBalanceBefore.plus(ethSpentOnFee));
- expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
- expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount),
- );
- expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
- erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFillAmount),
- );
- expect(newBalances[makerAddress][weth.address]).to.be.bignumber.equal(
- erc20Balances[makerAddress][weth.address].plus(primaryTakerAssetFillAmount),
- );
- expect(newBalances[forwarderContract.address][weth.address]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(newBalances[forwarderContract.address][defaultMakerAssetAddress]).to.be.bignumber.equal(
- constants.ZERO_AMOUNT,
- );
- expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should fail if the fee is set too high', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount;
-
- const baseFeePercentage = 6;
- feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage);
- await expectTransactionFailedAsync(
- forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- makerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- { feePercentage, feeRecipient: feeRecipientAddress },
- ),
- RevertReason.FeePercentageTooLarge,
- );
- });
- it('should fail if there is not enough ETH remaining to pay the fee', async () => {
- const ordersWithoutFee = [orderWithoutFee];
- const feeOrders: SignedOrder[] = [];
- const makerAssetFillAmount = orderWithoutFee.makerAssetAmount.dividedToIntegerBy(2);
- const ethValue = orderWithoutFee.takerAssetAmount.dividedToIntegerBy(2);
-
- const baseFeePercentage = 2;
- feePercentage = ForwarderWrapper.getPercentageOfValue(constants.PERCENTAGE_DENOMINATOR, baseFeePercentage);
- await expectTransactionFailedAsync(
- forwarderWrapper.marketBuyOrdersWithEthAsync(
- ordersWithoutFee,
- feeOrders,
- makerAssetFillAmount,
- {
- value: ethValue,
- from: takerAddress,
- },
- { feePercentage, feeRecipient: feeRecipientAddress },
- ),
- RevertReason.InsufficientEthRemaining,
- );
- });
- });
- describe('withdrawAsset', () => {
- it('should allow owner to withdraw ERC20 tokens', async () => {
- const zrxWithdrawAmount = erc20Balances[forwarderContract.address][zrxToken.address];
- await forwarderWrapper.withdrawAssetAsync(zrxAssetData, zrxWithdrawAmount, { from: owner });
- const newBalances = await erc20Wrapper.getBalancesAsync();
- expect(newBalances[owner][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[owner][zrxToken.address].plus(zrxWithdrawAmount),
- );
- expect(newBalances[forwarderContract.address][zrxToken.address]).to.be.bignumber.equal(
- erc20Balances[forwarderContract.address][zrxToken.address].minus(zrxWithdrawAmount),
- );
- });
- it('should revert if not called by owner', async () => {
- const zrxWithdrawAmount = erc20Balances[forwarderContract.address][zrxToken.address];
- await expectTransactionFailedAsync(
- forwarderWrapper.withdrawAssetAsync(zrxAssetData, zrxWithdrawAmount, { from: makerAddress }),
- RevertReason.OnlyContractOwner,
- );
- });
- });
-});
-// tslint:disable:max-file-line-count
-// tslint:enable:no-unnecessary-type-assertion
diff --git a/contracts/core/test/extensions/order_validator.ts b/contracts/core/test/extensions/order_validator.ts
deleted file mode 100644
index 622710c98..000000000
--- a/contracts/core/test/extensions/order_validator.ts
+++ /dev/null
@@ -1,606 +0,0 @@
-import {
- chaiSetup,
- constants,
- OrderFactory,
- OrderStatus,
- provider,
- txDefaults,
- web3Wrapper,
-} from '@0x/contracts-test-utils';
-import { DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens';
-import { BlockchainLifecycle } from '@0x/dev-utils';
-import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import * as chai from 'chai';
-import * as _ from 'lodash';
-
-import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy';
-import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy';
-import { ExchangeContract } from '../../generated-wrappers/exchange';
-import { OrderValidatorContract } from '../../generated-wrappers/order_validator';
-import { artifacts } from '../../src/artifacts';
-import { ERC20Wrapper } from '../utils/erc20_wrapper';
-import { ERC721Wrapper } from '../utils/erc721_wrapper';
-import { ExchangeWrapper } from '../utils/exchange_wrapper';
-
-chaiSetup.configure();
-const expect = chai.expect;
-const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-describe('OrderValidator', () => {
- let makerAddress: string;
- let owner: string;
- let takerAddress: string;
- let erc20AssetData: string;
- let erc721AssetData: string;
-
- let erc20Token: DummyERC20TokenContract;
- let zrxToken: DummyERC20TokenContract;
- let erc721Token: DummyERC721TokenContract;
- let exchange: ExchangeContract;
- let orderValidator: OrderValidatorContract;
- let erc20Proxy: ERC20ProxyContract;
- let erc721Proxy: ERC721ProxyContract;
-
- let signedOrder: SignedOrder;
- let signedOrder2: SignedOrder;
- let orderFactory: OrderFactory;
-
- const tokenId = new BigNumber(123456789);
- const tokenId2 = new BigNumber(987654321);
- const ERC721_BALANCE = new BigNumber(1);
- const ERC721_ALLOWANCE = new BigNumber(1);
-
- before(async () => {
- await blockchainLifecycle.startAsync();
- });
- after(async () => {
- await blockchainLifecycle.revertAsync();
- });
-
- before(async () => {
- const accounts = await web3Wrapper.getAvailableAddressesAsync();
- const usedAddresses = ([owner, makerAddress, takerAddress] = _.slice(accounts, 0, 3));
-
- const erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
- const erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
-
- const numDummyErc20ToDeploy = 2;
- [erc20Token, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
- numDummyErc20ToDeploy,
- constants.DUMMY_TOKEN_DECIMALS,
- );
- erc20Proxy = await erc20Wrapper.deployProxyAsync();
-
- [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
- erc721Proxy = await erc721Wrapper.deployProxyAsync();
-
- const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
- exchange = await ExchangeContract.deployFrom0xArtifactAsync(
- artifacts.Exchange,
- provider,
- txDefaults,
- zrxAssetData,
- );
- const exchangeWrapper = new ExchangeWrapper(exchange, provider);
- await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
- await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
-
- orderValidator = await OrderValidatorContract.deployFrom0xArtifactAsync(
- artifacts.OrderValidator,
- provider,
- txDefaults,
- exchange.address,
- zrxAssetData,
- );
-
- erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20Token.address);
- erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId);
- const defaultOrderParams = {
- ...constants.STATIC_ORDER_PARAMS,
- exchangeAddress: exchange.address,
- makerAddress,
- feeRecipientAddress: constants.NULL_ADDRESS,
- makerAssetData: erc20AssetData,
- takerAssetData: erc721AssetData,
- };
- const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
- orderFactory = new OrderFactory(privateKey, defaultOrderParams);
- });
-
- beforeEach(async () => {
- await blockchainLifecycle.startAsync();
- });
- afterEach(async () => {
- await blockchainLifecycle.revertAsync();
- });
-
- describe('getBalanceAndAllowance', () => {
- describe('getERC721TokenOwner', async () => {
- it('should return the null address when tokenId is not owned', async () => {
- const tokenOwner = await orderValidator.getERC721TokenOwner.callAsync(makerAddress, tokenId);
- expect(tokenOwner).to.be.equal(constants.NULL_ADDRESS);
- });
- it('should return the owner address when tokenId is owned', async () => {
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const tokenOwner = await orderValidator.getERC721TokenOwner.callAsync(erc721Token.address, tokenId);
- expect(tokenOwner).to.be.equal(makerAddress);
- });
- });
- describe('ERC20 assetData', () => {
- it('should return the correct balances and allowances when both values are 0', async () => {
- const [newBalance, newAllowance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc20AssetData,
- );
- expect(constants.ZERO_AMOUNT).to.be.bignumber.equal(newBalance);
- expect(constants.ZERO_AMOUNT).to.be.bignumber.equal(newAllowance);
- });
- it('should return the correct balance and allowance when both values are non-zero', async () => {
- const balance = new BigNumber(123);
- const allowance = new BigNumber(456);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, balance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [newBalance, newAllowance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc20AssetData,
- );
- expect(balance).to.be.bignumber.equal(newBalance);
- expect(allowance).to.be.bignumber.equal(newAllowance);
- });
- });
- describe('ERC721 assetData', () => {
- it('should return a balance of 0 when the tokenId is not owned by target', async () => {
- const [newBalance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc721AssetData,
- );
- expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return an allowance of 0 when no approval is set', async () => {
- const [, newAllowance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc721AssetData,
- );
- expect(newAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return a balance of 1 when the tokenId is owned by target', async () => {
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [newBalance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc721AssetData,
- );
- expect(newBalance).to.be.bignumber.equal(ERC721_BALANCE);
- });
- it('should return an allowance of 1 when ERC721Proxy is approved for all', async () => {
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [, newAllowance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc721AssetData,
- );
- expect(newAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- });
- it('should return an allowance of 0 when ERC721Proxy is approved for specific tokenId', async () => {
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.approve.sendTransactionAsync(erc721Proxy.address, tokenId, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [, newAllowance] = await orderValidator.getBalanceAndAllowance.callAsync(
- makerAddress,
- erc721AssetData,
- );
- expect(newAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- });
- });
- describe('getBalancesAndAllowances', () => {
- it('should return the correct balances and allowances when all values are 0', async () => {
- const [
- [erc20Balance, erc721Balance],
- [erc20Allowance, erc721Allowance],
- ] = await orderValidator.getBalancesAndAllowances.callAsync(makerAddress, [
- erc20AssetData,
- erc721AssetData,
- ]);
- expect(erc20Balance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(erc721Balance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(erc20Allowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(erc721Allowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return the correct balances and allowances when balances and allowances are non-zero', async () => {
- const balance = new BigNumber(123);
- const allowance = new BigNumber(456);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, balance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(makerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [
- [erc20Balance, erc721Balance],
- [erc20Allowance, erc721Allowance],
- ] = await orderValidator.getBalancesAndAllowances.callAsync(makerAddress, [
- erc20AssetData,
- erc721AssetData,
- ]);
- expect(erc20Balance).to.be.bignumber.equal(balance);
- expect(erc721Balance).to.be.bignumber.equal(ERC721_BALANCE);
- expect(erc20Allowance).to.be.bignumber.equal(allowance);
- expect(erc721Allowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- });
- });
- describe('getTraderInfo', () => {
- beforeEach(async () => {
- signedOrder = await orderFactory.newSignedOrderAsync();
- });
- it('should return the correct info when no balances or allowances are set', async () => {
- const traderInfo = await orderValidator.getTraderInfo.callAsync(signedOrder, takerAddress);
- expect(traderInfo.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return the correct info when balances and allowances are set', async () => {
- const makerBalance = new BigNumber(123);
- const makerAllowance = new BigNumber(456);
- const makerZrxBalance = new BigNumber(789);
- const takerZrxAllowance = new BigNumber(987);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const traderInfo = await orderValidator.getTraderInfo.callAsync(signedOrder, takerAddress);
- expect(traderInfo.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
- expect(traderInfo.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- });
- });
- describe('getTradersInfo', () => {
- beforeEach(async () => {
- signedOrder = await orderFactory.newSignedOrderAsync();
- signedOrder2 = await orderFactory.newSignedOrderAsync({
- takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId2),
- });
- });
- it('should return the correct info when no balances or allowances have been set', async () => {
- const orders = [signedOrder, signedOrder2];
- const takers = [takerAddress, takerAddress];
- const [traderInfo1, traderInfo2] = await orderValidator.getTradersInfo.callAsync(orders, takers);
- expect(traderInfo1.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return the correct info when balances and allowances are set', async () => {
- const makerBalance = new BigNumber(123);
- const makerAllowance = new BigNumber(456);
- const makerZrxBalance = new BigNumber(789);
- const takerZrxAllowance = new BigNumber(987);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId2),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const orders = [signedOrder, signedOrder2];
- const takers = [takerAddress, takerAddress];
- const [traderInfo1, traderInfo2] = await orderValidator.getTradersInfo.callAsync(orders, takers);
-
- expect(traderInfo1.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo1.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- expect(traderInfo2.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo2.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo2.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
- expect(traderInfo2.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- });
- });
- describe('getOrderAndTraderInfo', () => {
- beforeEach(async () => {
- signedOrder = await orderFactory.newSignedOrderAsync();
- });
- it('should return the correct info when no balances or allowances are set', async () => {
- const [orderInfo, traderInfo] = await orderValidator.getOrderAndTraderInfo.callAsync(
- signedOrder,
- takerAddress,
- );
- const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
- expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo.orderHash).to.be.equal(expectedOrderHash);
- expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return the correct info when balances and allowances are set', async () => {
- const makerBalance = new BigNumber(123);
- const makerAllowance = new BigNumber(456);
- const makerZrxBalance = new BigNumber(789);
- const takerZrxAllowance = new BigNumber(987);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const [orderInfo, traderInfo] = await orderValidator.getOrderAndTraderInfo.callAsync(
- signedOrder,
- takerAddress,
- );
- const expectedOrderHash = orderHashUtils.getOrderHashHex(signedOrder);
- expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo.orderHash).to.be.equal(expectedOrderHash);
- expect(orderInfo.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
- expect(traderInfo.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- });
- });
- describe('getOrdersAndTradersInfo', () => {
- beforeEach(async () => {
- signedOrder = await orderFactory.newSignedOrderAsync();
- signedOrder2 = await orderFactory.newSignedOrderAsync({
- takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId2),
- });
- });
- it('should return the correct info when no balances or allowances have been set', async () => {
- const orders = [signedOrder, signedOrder2];
- const takers = [takerAddress, takerAddress];
- const [
- [orderInfo1, orderInfo2],
- [traderInfo1, traderInfo2],
- ] = await orderValidator.getOrdersAndTradersInfo.callAsync(orders, takers);
- const expectedOrderHash1 = orderHashUtils.getOrderHashHex(signedOrder);
- const expectedOrderHash2 = orderHashUtils.getOrderHashHex(signedOrder2);
- expect(orderInfo1.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo1.orderHash).to.be.equal(expectedOrderHash1);
- expect(orderInfo1.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(orderInfo2.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo2.orderHash).to.be.equal(expectedOrderHash2);
- expect(orderInfo2.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- });
- it('should return the correct info when balances and allowances are set', async () => {
- const makerBalance = new BigNumber(123);
- const makerAllowance = new BigNumber(456);
- const makerZrxBalance = new BigNumber(789);
- const takerZrxAllowance = new BigNumber(987);
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.setBalance.sendTransactionAsync(makerAddress, makerBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc20Token.approve.sendTransactionAsync(erc20Proxy.address, makerAllowance, {
- from: makerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.setBalance.sendTransactionAsync(makerAddress, makerZrxBalance),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, takerZrxAllowance, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const isApproved = true;
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, isApproved, {
- from: takerAddress,
- }),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- await web3Wrapper.awaitTransactionSuccessAsync(
- await erc721Token.mint.sendTransactionAsync(takerAddress, tokenId2),
- constants.AWAIT_TRANSACTION_MINED_MS,
- );
- const orders = [signedOrder, signedOrder2];
- const takers = [takerAddress, takerAddress];
- const [
- [orderInfo1, orderInfo2],
- [traderInfo1, traderInfo2],
- ] = await orderValidator.getOrdersAndTradersInfo.callAsync(orders, takers);
- const expectedOrderHash1 = orderHashUtils.getOrderHashHex(signedOrder);
- const expectedOrderHash2 = orderHashUtils.getOrderHashHex(signedOrder2);
- expect(orderInfo1.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo1.orderHash).to.be.equal(expectedOrderHash1);
- expect(orderInfo1.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(orderInfo2.orderStatus).to.be.equal(OrderStatus.FILLABLE);
- expect(orderInfo2.orderHash).to.be.equal(expectedOrderHash2);
- expect(orderInfo2.orderTakerAssetFilledAmount).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo1.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo1.takerBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo1.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo1.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo1.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- expect(traderInfo2.makerBalance).to.be.bignumber.equal(makerBalance);
- expect(traderInfo2.makerAllowance).to.be.bignumber.equal(makerAllowance);
- expect(traderInfo2.takerBalance).to.be.bignumber.equal(ERC721_BALANCE);
- expect(traderInfo2.takerAllowance).to.be.bignumber.equal(ERC721_ALLOWANCE);
- expect(traderInfo2.makerZrxBalance).to.be.bignumber.equal(makerZrxBalance);
- expect(traderInfo2.makerZrxAllowance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
- expect(traderInfo2.takerZrxAllowance).to.be.bignumber.equal(takerZrxAllowance);
- });
- });
-});
-// tslint:disable:max-file-line-count
diff --git a/contracts/core/test/tutorials/arbitrage.ts b/contracts/core/test/tutorials/arbitrage.ts
deleted file mode 100644
index 78e0bc238..000000000
--- a/contracts/core/test/tutorials/arbitrage.ts
+++ /dev/null
@@ -1,260 +0,0 @@
-// import { ECSignature, SignedOrder, ZeroEx } from '0x.js';
-// import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils';
-// import { ExchangeContractErrs } from 'ethereum-types';
-// import { BigNumber } from '@0x/utils';
-// import { Web3Wrapper } from '@0x/web3-wrapper';
-// import * as chai from 'chai';
-// import ethUtil = require('ethereumjs-util');
-// import * as Web3 from 'web3';
-
-// import { AccountLevelsContract } from '../../src/generated_contract_wrappers/account_levels';
-// import { ArbitrageContract } from '../../src/generated_contract_wrappers/arbitrage';
-// import { DummyTokenContract } from '../../src/generated_contract_wrappers/dummy_token';
-// import { EtherDeltaContract } from '../../src/generated_contract_wrappers/ether_delta';
-// import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
-// import { TokenTransferProxyContract } from '../../src/generated_contract_wrappers/token_transfer_proxy';
-// import { artifacts } from '../../util/artifacts';
-// import { Balances } from '../../util/balances';
-// import { constants } from '../../util/constants';
-// import { crypto } from '../../util/crypto';
-// import { ExchangeWrapper } from '../../util/exchange_wrapper';
-// import { OrderFactory } from '../../util/order_factory';
-// import { BalancesByOwner, ContractName } from '../../util/types';
-// import { chaiSetup } from '../utils/chai_setup';
-
-// import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
-
-// chaiSetup.configure();
-// const expect = chai.expect;
-// const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-
-// describe('Arbitrage', () => {
-// let coinbase: string;
-// let maker: string;
-// let edMaker: string;
-// let edFrontRunner: string;
-// let amountGet: BigNumber;
-// let amountGive: BigNumber;
-// let makerTokenAmount: BigNumber;
-// let takerTokenAmount: BigNumber;
-// const feeRecipient = constants.NULL_ADDRESS;
-// const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
-// const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
-
-// let weth: DummyTokenContract;
-// let zrx: DummyTokenContract;
-// let arbitrage: ArbitrageContract;
-// let etherDelta: EtherDeltaContract;
-
-// let signedOrder: SignedOrder;
-// let exWrapper: ExchangeWrapper;
-// let orderFactory: OrderFactory;
-
-// let zeroEx: ZeroEx;
-
-// // From a bird's eye view - we create two orders.
-// // 0x order of 1 ZRX (maker) for 1 WETH (taker)
-// // ED order of 2 WETH (tokenGive) for 1 ZRX (tokenGet)
-// // And then we do an atomic arbitrage between them which gives us 1 WETH.
-// before(async () => {
-// const accounts = await web3Wrapper.getAvailableAddressesAsync();
-// [coinbase, maker, edMaker, edFrontRunner] = accounts;
-// weth = await DummyTokenContract.deployFrom0xArtifactAsync(
-// artifacts.DummyToken,
-// provider,
-// txDefaults,
-// constants.DUMMY_TOKEN_NAME,
-// constants.DUMMY_TOKEN_SYMBOL,
-// constants.DUMMY_TOKEN_DECIMALS,
-// constants.DUMMY_TOKEN_TOTAL_SUPPLY,
-// );
-// zrx = await DummyTokenContract.deployFrom0xArtifactAsync(
-// artifacts.DummyToken,
-// provider,
-// txDefaults,
-// constants.DUMMY_TOKEN_NAME,
-// constants.DUMMY_TOKEN_SYMBOL,
-// constants.DUMMY_TOKEN_DECIMALS,
-// constants.DUMMY_TOKEN_TOTAL_SUPPLY,
-// );
-// const accountLevels = await AccountLevelsContract.deployFrom0xArtifactAsync(
-// artifacts.AccountLevels,
-// provider,
-// txDefaults,
-// );
-// const edAdminAddress = accounts[0];
-// const edMakerFee = new BigNumber(0);
-// const edTakerFee = new BigNumber(0);
-// const edFeeRebate = new BigNumber(0);
-// etherDelta = await EtherDeltaContract.deployFrom0xArtifactAsync(
-// artifacts.EtherDelta,
-// provider,
-// txDefaults,
-// edAdminAddress,
-// feeRecipient,
-// accountLevels.address,
-// edMakerFee,
-// edTakerFee,
-// edFeeRebate,
-// );
-// const tokenTransferProxy = await TokenTransferProxyContract.deployFrom0xArtifactAsync(
-// artifacts.TokenTransferProxy,
-// provider,
-// txDefaults,
-// );
-// const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
-// artifacts.Exchange,
-// provider,
-// txDefaults,
-// zrx.address,
-// tokenTransferProxy.address,
-// );
-// await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
-// zeroEx = new ZeroEx(provider, {
-// exchangeContractAddress: exchange.address,
-// networkId: constants.TESTRPC_NETWORK_ID,
-// });
-// exWrapper = new ExchangeWrapper(exchange, provider);
-
-// makerTokenAmount = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18);
-// takerTokenAmount = makerTokenAmount;
-// const defaultOrderParams = {
-// exchangeContractAddress: exchange.address,
-// maker,
-// feeRecipient,
-// makerTokenAddress: zrx.address,
-// takerTokenAddress: weth.address,
-// makerTokenAmount,
-// takerTokenAmount,
-// makerFee: new BigNumber(0),
-// takerFee: new BigNumber(0),
-// };
-// orderFactory = new OrderFactory(zeroEx, defaultOrderParams);
-// arbitrage = await ArbitrageContract.deployFrom0xArtifactAsync(
-// artifacts.Arbitrage,
-// provider,
-// txDefaults,
-// exchange.address,
-// etherDelta.address,
-// tokenTransferProxy.address,
-// );
-// // Enable arbitrage and withdrawals of tokens
-// await arbitrage.setAllowances.sendTransactionAsync(weth.address, { from: coinbase });
-// await arbitrage.setAllowances.sendTransactionAsync(zrx.address, { from: coinbase });
-
-// // Give some tokens to arbitrage contract
-// await weth.setBalance.sendTransactionAsync(arbitrage.address, takerTokenAmount, { from: coinbase });
-
-// // Fund the maker on exchange side
-// await zrx.setBalance.sendTransactionAsync(maker, makerTokenAmount, { from: coinbase });
-// // Set the allowance for the maker on Exchange side
-// await zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: maker });
-
-// amountGive = ZeroEx.toBaseUnitAmount(new BigNumber(2), 18);
-// // Fund the maker on EtherDelta side
-// await weth.setBalance.sendTransactionAsync(edMaker, amountGive, { from: coinbase });
-// // Set the allowance for the maker on EtherDelta side
-// await weth.approve.sendTransactionAsync(etherDelta.address, INITIAL_ALLOWANCE, { from: edMaker });
-// // Deposit maker funds into EtherDelta
-// await etherDelta.depositToken.sendTransactionAsync(weth.address, amountGive, { from: edMaker });
-
-// amountGet = makerTokenAmount;
-// // Fund the front runner on EtherDelta side
-// await zrx.setBalance.sendTransactionAsync(edFrontRunner, amountGet, { from: coinbase });
-// // Set the allowance for the front-runner on EtherDelta side
-// await zrx.approve.sendTransactionAsync(etherDelta.address, INITIAL_ALLOWANCE, { from: edFrontRunner });
-// // Deposit front runner funds into EtherDelta
-// await etherDelta.depositToken.sendTransactionAsync(zrx.address, amountGet, { from: edFrontRunner });
-// });
-// beforeEach(async () => {
-// await blockchainLifecycle.startAsync();
-// });
-// afterEach(async () => {
-// await blockchainLifecycle.revertAsync();
-// });
-// describe('makeAtomicTrade', () => {
-// let addresses: string[];
-// let values: BigNumber[];
-// let v: number[];
-// let r: string[];
-// let s: string[];
-// let tokenGet: string;
-// let tokenGive: string;
-// let expires: BigNumber;
-// let nonce: BigNumber;
-// let edSignature: ECSignature;
-// before(async () => {
-// signedOrder = await orderFactory.newSignedOrderAsync();
-// tokenGet = zrx.address;
-// tokenGive = weth.address;
-// const blockNumber = await web3Wrapper.getBlockNumberAsync();
-// const ED_ORDER_EXPIRATION_IN_BLOCKS = 10;
-// expires = new BigNumber(blockNumber + ED_ORDER_EXPIRATION_IN_BLOCKS);
-// nonce = new BigNumber(42);
-// const edOrderHash = `0x${crypto
-// .solSHA256([etherDelta.address, tokenGet, amountGet, tokenGive, amountGive, expires, nonce])
-// .toString('hex')}`;
-// const shouldAddPersonalMessagePrefix = false;
-// edSignature = await zeroEx.signOrderHashAsync(edOrderHash, edMaker, shouldAddPersonalMessagePrefix);
-// addresses = [
-// signedOrder.maker,
-// signedOrder.taker,
-// signedOrder.makerTokenAddress,
-// signedOrder.takerTokenAddress,
-// signedOrder.feeRecipient,
-// edMaker,
-// ];
-// const fillTakerTokenAmount = takerTokenAmount;
-// const edFillAmount = makerTokenAmount;
-// values = [
-// signedOrder.makerTokenAmount,
-// signedOrder.takerTokenAmount,
-// signedOrder.makerFee,
-// signedOrder.takerFee,
-// signedOrder.expirationUnixTimestampSec,
-// signedOrder.salt,
-// fillTakerTokenAmount,
-// amountGet,
-// amountGive,
-// expires,
-// nonce,
-// edFillAmount,
-// ];
-// v = [signedOrder.ecSignature.v, edSignature.v];
-// r = [signedOrder.ecSignature.r, edSignature.r];
-// s = [signedOrder.ecSignature.s, edSignature.s];
-// });
-// it('should successfully execute the arbitrage if not front-runned', async () => {
-// const txHash = await arbitrage.makeAtomicTrade.sendTransactionAsync(addresses, values, v, r, s, {
-// from: coinbase,
-// });
-// const res = await zeroEx.awaitTransactionMinedAsync(txHash);
-// const postBalance = await weth.balanceOf.callAsync(arbitrage.address);
-// expect(postBalance).to.be.bignumber.equal(amountGive);
-// });
-// it('should fail and revert if front-runned', async () => {
-// const preBalance = await weth.balanceOf.callAsync(arbitrage.address);
-// // Front-running transaction
-// await etherDelta.trade.sendTransactionAsync(
-// tokenGet,
-// amountGet,
-// tokenGive,
-// amountGive,
-// expires,
-// nonce,
-// edMaker,
-// edSignature.v,
-// edSignature.r,
-// edSignature.s,
-// amountGet,
-// { from: edFrontRunner },
-// );
-// // tslint:disable-next-line:await-promise
-// await expect(
-// arbitrage.makeAtomicTrade.sendTransactionAsync(addresses, values, v, r, s, { from: coinbase }),
-// ).to.be.rejectedWith(constants.REVERT);
-// const postBalance = await weth.balanceOf.callAsync(arbitrage.address);
-// expect(preBalance).to.be.bignumber.equal(postBalance);
-// });
-// });
-// });
diff --git a/contracts/core/test/utils/forwarder_wrapper.ts b/contracts/core/test/utils/forwarder_wrapper.ts
deleted file mode 100644
index 4c78ecd79..000000000
--- a/contracts/core/test/utils/forwarder_wrapper.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { constants, formatters, LogDecoder, MarketSellOrders } from '@0x/contracts-test-utils';
-import { artifacts as tokensArtifacts } from '@0x/contracts-tokens';
-import { SignedOrder } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import { Provider, TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types';
-import * as _ from 'lodash';
-
-import { ForwarderContract } from '../../generated-wrappers/forwarder';
-import { artifacts } from '../../src/artifacts';
-
-export class ForwarderWrapper {
- private readonly _web3Wrapper: Web3Wrapper;
- private readonly _forwarderContract: ForwarderContract;
- private readonly _logDecoder: LogDecoder;
- public static getPercentageOfValue(value: BigNumber, percentage: number): BigNumber {
- const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100);
- const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR);
- return newValue;
- }
- public static getWethForFeeOrders(feeAmount: BigNumber, feeOrders: SignedOrder[]): BigNumber {
- let wethAmount = new BigNumber(0);
- let remainingFeeAmount = feeAmount;
- _.forEach(feeOrders, feeOrder => {
- const feeAvailable = feeOrder.makerAssetAmount.minus(feeOrder.takerFee);
- if (!remainingFeeAmount.isZero() && feeAvailable.gt(remainingFeeAmount)) {
- wethAmount = wethAmount.plus(
- feeOrder.takerAssetAmount
- .times(remainingFeeAmount)
- .dividedBy(feeAvailable)
- .ceil(),
- );
- remainingFeeAmount = new BigNumber(0);
- } else if (!remainingFeeAmount.isZero()) {
- wethAmount = wethAmount.plus(feeOrder.takerAssetAmount);
- remainingFeeAmount = remainingFeeAmount.minus(feeAvailable);
- }
- });
- return wethAmount;
- }
- private static _createOptimizedOrders(signedOrders: SignedOrder[]): MarketSellOrders {
- _.forEach(signedOrders, (signedOrder, index) => {
- signedOrder.takerAssetData = constants.NULL_BYTES;
- if (index > 0) {
- signedOrder.makerAssetData = constants.NULL_BYTES;
- }
- });
- const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT);
- return params;
- }
- private static _createOptimizedZrxOrders(signedOrders: SignedOrder[]): MarketSellOrders {
- _.forEach(signedOrders, signedOrder => {
- signedOrder.makerAssetData = constants.NULL_BYTES;
- signedOrder.takerAssetData = constants.NULL_BYTES;
- });
- const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT);
- return params;
- }
- constructor(contractInstance: ForwarderContract, provider: Provider) {
- this._forwarderContract = contractInstance;
- this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, { ...artifacts, ...tokensArtifacts });
- }
- public async marketSellOrdersWithEthAsync(
- orders: SignedOrder[],
- feeOrders: SignedOrder[],
- txData: TxDataPayable,
- opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
- ): Promise<TransactionReceiptWithDecodedLogs> {
- const params = ForwarderWrapper._createOptimizedOrders(orders);
- const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders);
- const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage;
- const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient;
- const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync(
- params.orders,
- params.signatures,
- feeParams.orders,
- feeParams.signatures,
- feePercentage,
- feeRecipient,
- txData,
- );
- const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
- return tx;
- }
- public async marketBuyOrdersWithEthAsync(
- orders: SignedOrder[],
- feeOrders: SignedOrder[],
- makerAssetFillAmount: BigNumber,
- txData: TxDataPayable,
- opts: { feePercentage?: BigNumber; feeRecipient?: string } = {},
- ): Promise<TransactionReceiptWithDecodedLogs> {
- const params = ForwarderWrapper._createOptimizedOrders(orders);
- const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders);
- const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage;
- const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient;
- const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync(
- params.orders,
- makerAssetFillAmount,
- params.signatures,
- feeParams.orders,
- feeParams.signatures,
- feePercentage,
- feeRecipient,
- txData,
- );
- const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
- return tx;
- }
- public async withdrawAssetAsync(
- assetData: string,
- amount: BigNumber,
- txData: TxDataPayable,
- ): Promise<TransactionReceiptWithDecodedLogs> {
- const txHash = await this._forwarderContract.withdrawAsset.sendTransactionAsync(assetData, amount, txData);
- const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
- return tx;
- }
-}
diff --git a/contracts/core/test/utils/index.ts b/contracts/core/test/utils/index.ts
new file mode 100644
index 000000000..75cd88666
--- /dev/null
+++ b/contracts/core/test/utils/index.ts
@@ -0,0 +1,3 @@
+export * from './exchange_wrapper';
+export * from './erc20_wrapper';
+export * from './erc721_wrapper';
diff --git a/contracts/core/tsconfig.json b/contracts/core/tsconfig.json
index d6adef2c5..db872fc32 100644
--- a/contracts/core/tsconfig.json
+++ b/contracts/core/tsconfig.json
@@ -8,14 +8,11 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"./generated-artifacts/AssetProxyOwner.json",
- "./generated-artifacts/DutchAuction.json",
"./generated-artifacts/ERC20Proxy.json",
"./generated-artifacts/ERC721Proxy.json",
"./generated-artifacts/Exchange.json",
- "./generated-artifacts/Forwarder.json",
"./generated-artifacts/MixinAuthorizable.json",
"./generated-artifacts/MultiAssetProxy.json",
- "./generated-artifacts/OrderValidator.json",
"./generated-artifacts/TestAssetProxyDispatcher.json",
"./generated-artifacts/TestAssetProxyOwner.json",
"./generated-artifacts/TestExchangeInternals.json",