aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src/2.0.0')
-rw-r--r--packages/contracts/src/2.0.0/examples/ExchangeWrapper/ExchangeWrapper.sol (renamed from packages/contracts/src/2.0.0/test/ExchangeWrapper/ExchangeWrapper.sol)0
-rw-r--r--packages/contracts/src/2.0.0/examples/Validator/Validator.sol (renamed from packages/contracts/src/2.0.0/test/TestValidator/TestValidator.sol)4
-rw-r--r--packages/contracts/src/2.0.0/examples/Wallet/Wallet.sol (renamed from packages/contracts/src/2.0.0/test/TestWallet/TestWallet.sol)4
-rw-r--r--packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol (renamed from packages/contracts/src/2.0.0/test/Whitelist/Whitelist.sol)0
-rw-r--r--packages/contracts/src/2.0.0/forwarder/Forwarder.sol21
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinAssets.sol (renamed from packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol)89
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol35
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol253
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinExpectedResults.sol161
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinFees.sol126
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol499
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinMarketBuyZrx.sol83
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinWeth.sol110
-rw-r--r--packages/contracts/src/2.0.0/forwarder/interfaces/IAssets.sol (renamed from packages/contracts/src/2.0.0/forwarder/mixins/MTransfer.sol)30
-rw-r--r--packages/contracts/src/2.0.0/forwarder/interfaces/IExpectedResults.sol66
-rw-r--r--packages/contracts/src/2.0.0/forwarder/interfaces/IForwarder.sol4
-rw-r--r--packages/contracts/src/2.0.0/forwarder/interfaces/IForwarderCore.sol69
-rw-r--r--packages/contracts/src/2.0.0/forwarder/libs/LibConstants.sol (renamed from packages/contracts/src/2.0.0/forwarder/MixinConstants.sol)27
-rw-r--r--packages/contracts/src/2.0.0/forwarder/libs/LibForwarderErrors.sol34
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol54
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol35
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol87
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MExpectedResults.sol42
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MFees.sol63
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol68
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MMarketBuyZrx.sol42
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MWeth.sol41
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol100
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol2
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol227
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol218
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol13
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol6
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol28
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol2
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol5
-rw-r--r--packages/contracts/src/2.0.0/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol4
-rw-r--r--packages/contracts/src/2.0.0/test/TestConstants/TestConstants.sol57
-rw-r--r--packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol2
39 files changed, 1269 insertions, 1442 deletions
diff --git a/packages/contracts/src/2.0.0/test/ExchangeWrapper/ExchangeWrapper.sol b/packages/contracts/src/2.0.0/examples/ExchangeWrapper/ExchangeWrapper.sol
index 2fa0e3c5e..2fa0e3c5e 100644
--- a/packages/contracts/src/2.0.0/test/ExchangeWrapper/ExchangeWrapper.sol
+++ b/packages/contracts/src/2.0.0/examples/ExchangeWrapper/ExchangeWrapper.sol
diff --git a/packages/contracts/src/2.0.0/test/TestValidator/TestValidator.sol b/packages/contracts/src/2.0.0/examples/Validator/Validator.sol
index 6278aede0..72ed528ba 100644
--- a/packages/contracts/src/2.0.0/test/TestValidator/TestValidator.sol
+++ b/packages/contracts/src/2.0.0/examples/Validator/Validator.sol
@@ -21,7 +21,7 @@ pragma solidity 0.4.24;
import "../../protocol/Exchange/interfaces/IValidator.sol";
-contract TestValidator is
+contract Validator is
IValidator
{
@@ -29,7 +29,7 @@ contract TestValidator is
// solhint-disable-next-line var-name-mixedcase
address internal VALID_SIGNER;
- /// @dev constructs a new `TestValidator` with a single valid signer.
+ /// @dev constructs a new `Validator` with a single valid signer.
/// @param validSigner The sole, valid signer.
constructor (address validSigner) public {
VALID_SIGNER = validSigner;
diff --git a/packages/contracts/src/2.0.0/test/TestWallet/TestWallet.sol b/packages/contracts/src/2.0.0/examples/Wallet/Wallet.sol
index 0415823e3..b75021a31 100644
--- a/packages/contracts/src/2.0.0/test/TestWallet/TestWallet.sol
+++ b/packages/contracts/src/2.0.0/examples/Wallet/Wallet.sol
@@ -22,7 +22,7 @@ import "../../protocol/Exchange/interfaces/IWallet.sol";
import "../../utils/LibBytes/LibBytes.sol";
-contract TestWallet is
+contract Wallet is
IWallet
{
using LibBytes for bytes;
@@ -31,7 +31,7 @@ contract TestWallet is
// solhint-disable-next-line var-name-mixedcase
address internal WALLET_OWNER;
- /// @dev constructs a new `TestWallet` with a single owner.
+ /// @dev constructs a new `Wallet` with a single owner.
/// @param walletOwner The owner of this wallet.
constructor (address walletOwner) public {
WALLET_OWNER = walletOwner;
diff --git a/packages/contracts/src/2.0.0/test/Whitelist/Whitelist.sol b/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol
index 60cac26ea..60cac26ea 100644
--- a/packages/contracts/src/2.0.0/test/Whitelist/Whitelist.sol
+++ b/packages/contracts/src/2.0.0/examples/Whitelist/Whitelist.sol
diff --git a/packages/contracts/src/2.0.0/forwarder/Forwarder.sol b/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
index 546e7f22c..5b88b05b1 100644
--- a/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
+++ b/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
@@ -19,20 +19,19 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "./MixinFees.sol";
+import "./MixinWeth.sol";
import "./MixinForwarderCore.sol";
-import "./MixinConstants.sol";
-import "./MixinMarketBuyZrx.sol";
-import "./MixinExpectedResults.sol";
-import "./MixinTransfer.sol";
+import "./libs/LibConstants.sol";
+import "./MixinAssets.sol";
+import "./MixinExchangeWrapper.sol";
+// solhint-disable no-empty-blocks
contract Forwarder is
- MixinConstants,
- MixinExpectedResults,
- MixinFees,
- MixinMarketBuyZrx,
- MixinTransfer,
+ LibConstants,
+ MixinWeth,
+ MixinAssets,
+ MixinExchangeWrapper,
MixinForwarderCore
{
@@ -44,7 +43,7 @@ contract Forwarder is
bytes memory _wethAssetData
)
public
- MixinConstants(
+ LibConstants(
_exchange,
_etherToken,
_zrxToken,
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol b/packages/contracts/src/2.0.0/forwarder/MixinAssets.sol
index 6c49330f2..5cf5f831b 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinAssets.sol
@@ -19,58 +19,78 @@
pragma solidity 0.4.24;
import "../utils/LibBytes/LibBytes.sol";
+import "../utils/Ownable/Ownable.sol";
+import "../tokens/ERC20Token/IERC20Token.sol";
import "../tokens/ERC721Token/IERC721Token.sol";
-import "./mixins/MTransfer.sol";
+import "./libs/LibConstants.sol";
+import "./mixins/MAssets.sol";
-contract MixinTransfer is
- MTransfer
+contract MixinAssets is
+ Ownable,
+ LibConstants,
+ MAssets
{
using LibBytes for bytes;
bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
- bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)"));
- bytes4 constant internal ERC721_RECEIVED_OPERATOR = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
- function onERC721Received(
- address,
- uint256,
- bytes memory
+ /// @dev Withdraws ERC20 tokens 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 tokens that were accidentally sent to this contract.
+ /// @param token Address of ERC20 token to withdraw.
+ /// @param amount Amount of ERC20 token to withdraw.
+ function withdrawERC20(
+ address token,
+ uint256 amount
)
- public
- pure
- returns(bytes4)
+ external
+ onlyOwner
{
- return ERC721_RECEIVED;
+ require(
+ IERC20Token(token).transfer(msg.sender, amount),
+ "WITHDRAWAL_FAILED"
+ );
}
- function onERC721Received(
- address,
- address,
- uint256,
- bytes memory
+ /// @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 transferPurchasedAssetToSender(
+ bytes memory assetData,
+ uint256 amount
)
- public
- pure
- returns(bytes4)
+ internal
{
- return ERC721_RECEIVED_OPERATOR;
+ 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_TOKEN_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(
- address token,
- address to,
+ 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,
- to,
+ msg.sender,
amount
));
require(
@@ -100,21 +120,28 @@ contract MixinTransfer is
);
}
+ /// @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,
- address to
+ uint256 amount
)
internal
{
+ require(
+ amount == 1,
+ "INVALID_AMOUNT"
+ );
// Decode asset data.
address token = assetData.readAddress(16);
uint256 tokenId = assetData.readUint256(36);
- bytes memory receiverData = assetData.readBytesWithLength(100);
- IERC721Token(token).safeTransferFrom(
+
+ // Perform transfer.
+ IERC721Token(token).transferFrom(
address(this),
- to,
- tokenId,
- receiverData
+ msg.sender,
+ tokenId
);
}
}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol b/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol
deleted file mode 100644
index 1b3e3f488..000000000
--- a/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol
+++ /dev/null
@@ -1,35 +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 MixinErrorMessages {
- string constant VALUE_GREATER_THAN_ZERO = "VALUE_GREATER_THAN_ZERO";
- string constant FEE_PROPORTION_TOO_LARGE = "FEE_PROPORTION_TOO_LARGE";
- string constant TAKER_ASSET_ZRX_REQUIRED = "TAKER_ASSET_ZRX_REQUIRED";
- string constant TAKER_ASSET_WETH_REQUIRED = "TAKER_ASSET_WETH_REQUIRED";
- string constant SAME_ASSET_TYPE_REQUIRED = "SAME_ASSET_TYPE_REQUIRED";
- string constant UNACCEPTABLE_THRESHOLD = "UNACCEPTABLE_THRESHOLD";
- string constant UNSUPPORTED_TOKEN_PROXY = "UNSUPPORTED_TOKEN_PROXY";
- string constant ASSET_AMOUNT_MATCH_ORDER_SIZE = "ASSET_AMOUNT_MUST_MATCH_ORDER_SIZE";
- string constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY = "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY";
- string constant INVALID_MSG_VALUE = "INVALID_MSG_VALUE";
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol b/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol
new file mode 100644
index 000000000..f3aa483c5
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol
@@ -0,0 +1,253 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "./libs/LibConstants.sol";
+import "./mixins/MExchangeWrapper.sol";
+import "../protocol/Exchange/libs/LibAbiEncoder.sol";
+import "../protocol/Exchange/libs/LibOrder.sol";
+import "../protocol/Exchange/libs/LibFillResults.sol";
+import "../protocol/Exchange/libs/LibMath.sol";
+
+
+contract MixinExchangeWrapper is
+ LibAbiEncoder,
+ LibFillResults,
+ LibMath,
+ LibConstants,
+ MExchangeWrapper
+{
+
+ /// @dev Fills the input order.
+ /// Returns false if the transaction would otherwise revert.
+ /// @param order Order struct containing order specifications.
+ /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
+ /// @param signature Proof that order has been created by maker.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function fillOrderNoThrow(
+ LibOrder.Order memory order,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ internal
+ returns (FillResults memory fillResults)
+ {
+ // ABI encode calldata for `fillOrder`
+ bytes memory fillOrderCalldata = abiEncodeFillOrder(
+ order,
+ takerAssetFillAmount,
+ signature
+ );
+
+ address exchange = address(EXCHANGE);
+
+ // Call `fillOrder` and handle any exceptions gracefully
+ assembly {
+ let success := call(
+ gas, // forward all gas, TODO: look into gas consumption of assert/throw
+ exchange, // call address of Exchange contract
+ 0, // transfer 0 wei
+ add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)
+ mload(fillOrderCalldata), // length of input
+ fillOrderCalldata, // write output over input
+ 128 // output size is 128 bytes
+ )
+ switch success
+ case 0 {
+ mstore(fillResults, 0)
+ mstore(add(fillResults, 32), 0)
+ mstore(add(fillResults, 64), 0)
+ mstore(add(fillResults, 96), 0)
+ }
+ case 1 {
+ mstore(fillResults, mload(fillOrderCalldata))
+ mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
+ mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
+ mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))
+ }
+ }
+ return fillResults;
+ }
+
+ /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.
+ /// Returns false if the transaction would otherwise revert.
+ /// @param orders Array of order specifications.
+ /// @param wethSellAmount Desired amount of WETH to sell.
+ /// @param signatures Proofs that orders have been signed by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ function marketSellWeth(
+ LibOrder.Order[] memory orders,
+ uint256 wethSellAmount,
+ bytes[] memory signatures
+ )
+ internal
+ returns (FillResults memory totalFillResults)
+ {
+ bytes memory makerAssetData = orders[0].makerAssetData;
+ bytes memory wethAssetData = WETH_ASSET_DATA;
+
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
+
+ // We assume that asset being bought by taker is the same for each order.
+ // We assume that asset being sold by taker is WETH for each order.
+ orders[i].makerAssetData = makerAssetData;
+ orders[i].takerAssetData = wethAssetData;
+
+ // Calculate the remaining amount of WETH to sell
+ uint256 remainingTakerAssetFillAmount = safeSub(wethSellAmount, totalFillResults.takerAssetFilledAmount);
+
+ // Attempt to sell the remaining amount of WETH
+ FillResults memory singleFillResults = fillOrderNoThrow(
+ orders[i],
+ remainingTakerAssetFillAmount,
+ signatures[i]
+ );
+
+ // Update amounts filled and fees paid by maker and taker
+ addFillResults(totalFillResults, singleFillResults);
+
+ // Stop execution if the entire amount of takerAsset has been sold
+ if (totalFillResults.takerAssetFilledAmount >= wethSellAmount) {
+ break;
+ }
+ }
+ return totalFillResults;
+ }
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.
+ /// Returns false if the transaction would otherwise revert.
+ /// The asset being sold by taker must always be WETH.
+ /// @param orders Array of order specifications.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
+ /// @param signatures Proofs that orders have been signed by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ function marketBuyWithWeth(
+ LibOrder.Order[] memory orders,
+ uint256 makerAssetFillAmount,
+ bytes[] memory signatures
+ )
+ internal
+ returns (FillResults memory totalFillResults)
+ {
+ bytes memory makerAssetData = orders[0].makerAssetData;
+ bytes memory wethAssetData = WETH_ASSET_DATA;
+
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
+
+ // We assume that asset being bought by taker is the same for each order.
+ // We assume that asset being sold by taker is WETH for each order.
+ orders[i].makerAssetData = makerAssetData;
+ orders[i].takerAssetData = wethAssetData;
+
+ // Calculate the remaining amount of makerAsset to buy
+ uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
+
+ // Convert the remaining amount of makerAsset to buy into remaining amount
+ // of takerAsset to sell, assuming entire amount can be sold in the current order
+ uint256 remainingTakerAssetFillAmount = getPartialAmount(
+ orders[i].takerAssetAmount,
+ orders[i].makerAssetAmount,
+ remainingMakerAssetFillAmount
+ );
+
+ // Attempt to sell the remaining amount of takerAsset
+ FillResults memory singleFillResults = fillOrderNoThrow(
+ orders[i],
+ remainingTakerAssetFillAmount,
+ signatures[i]
+ );
+
+ // Update amounts filled and fees paid by maker and taker
+ addFillResults(totalFillResults, singleFillResults);
+
+ // Stop execution if the entire amount of makerAsset has been bought
+ if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
+ break;
+ }
+ }
+ 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 marketBuyZrxWithWeth(
+ LibOrder.Order[] memory orders,
+ uint256 zrxBuyAmount,
+ bytes[] memory signatures
+ )
+ internal
+ returns (FillResults memory totalFillResults)
+ {
+ // Do nothing if zrxBuyAmount == 0
+ if (zrxBuyAmount == 0) {
+ return totalFillResults;
+ }
+
+ bytes memory zrxAssetData = ZRX_ASSET_DATA;
+ bytes memory wethAssetData = WETH_ASSET_DATA;
+ uint256 zrxPurchased = 0;
+
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
+
+ // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.
+ orders[i].makerAssetData = zrxAssetData;
+ orders[i].takerAssetData = wethAssetData;
+
+ // Calculate the remaining amount of ZRX to buy.
+ uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, zrxPurchased);
+
+ // Convert the remaining amount of ZRX to buy into remaining amount
+ // of WETH to sell, assuming entire amount can be sold in the current order.
+ uint256 remainingWethSellAmount = getPartialAmount(
+ orders[i].takerAssetAmount,
+ safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
+ remainingZrxBuyAmount
+ );
+
+ // Attempt to sell the remaining amount of WETH.
+ FillResults memory singleFillResult = fillOrderNoThrow(
+ orders[i],
+ safeAdd(remainingWethSellAmount, 1), // we add 1 wei to the fill amount to make up for rounding errors
+ signatures[i]
+ );
+
+ // Update amounts filled and fees paid by maker and taker.
+ addFillResults(totalFillResults, singleFillResult);
+ zrxPurchased = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
+
+ // Stop execution if the entire amount of ZRX has been bought.
+ if (zrxPurchased >= zrxBuyAmount) {
+ break;
+ }
+ }
+
+ return totalFillResults;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinExpectedResults.sol b/packages/contracts/src/2.0.0/forwarder/MixinExpectedResults.sol
deleted file mode 100644
index a575c9675..000000000
--- a/packages/contracts/src/2.0.0/forwarder/MixinExpectedResults.sol
+++ /dev/null
@@ -1,161 +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 "../utils/LibBytes/LibBytes.sol";
-import "../protocol/Exchange/libs/LibFillResults.sol";
-import "../protocol/Exchange/libs/LibMath.sol";
-import "../protocol/Exchange/libs/LibOrder.sol";
-import "./mixins/MConstants.sol";
-import "./mixins/MExpectedResults.sol";
-
-
-contract MixinExpectedResults is
- LibMath,
- LibFillResults,
- MConstants,
- MExpectedResults
-{
-
- /// @dev Calculates a total FillResults for buying makerAssetFillAmount over all orders.
- /// Including the fees required to be paid.
- /// @param orders An array of Order struct containing order specifications.
- /// @param makerAssetFillAmount A number representing the amount of this order to fill.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function calculateMarketBuyResults(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount
- )
- public
- view
- returns (FillResults memory totalFillResults)
- {
- for (uint256 i = 0; i < orders.length; i++) {
- uint256 remainingMakerAssetFillAmount = safeSub(makerAssetFillAmount, totalFillResults.makerAssetFilledAmount);
- uint256 remainingTakerAssetFillAmount = getPartialAmount(
- orders[i].takerAssetAmount,
- orders[i].makerAssetAmount,
- remainingMakerAssetFillAmount
- );
- FillResults memory singleFillResult = calculateFillResults(orders[i], remainingTakerAssetFillAmount);
- addFillResults(totalFillResults, singleFillResult);
- if (totalFillResults.makerAssetFilledAmount == makerAssetFillAmount) {
- break;
- }
- }
- return totalFillResults;
- }
-
- /// @dev Calculates a FillResults total for selling takerAssetFillAmount over all orders.
- /// Including the fees required to be paid.
- /// @param orders An array of Order struct containing order specifications.
- /// @param takerAssetFillAmount A number representing the amount of this order to fill.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function calculateMarketSellResults(
- LibOrder.Order[] memory orders,
- uint256 takerAssetFillAmount
- )
- public
- view
- returns (FillResults memory totalFillResults)
- {
- for (uint256 i = 0; i < orders.length; i++) {
- uint256 remainingTakerAssetFillAmount = safeSub(takerAssetFillAmount, totalFillResults.takerAssetFilledAmount);
- FillResults memory singleFillResult = calculateFillResults(orders[i], remainingTakerAssetFillAmount);
- addFillResults(totalFillResults, singleFillResult);
- if (totalFillResults.takerAssetFilledAmount == takerAssetFillAmount) {
- break;
- }
- }
- return totalFillResults;
- }
-
- /// @dev Calculates fill results for buyFeeTokens. This handles fees on buying ZRX
- /// so the end result is the expected amount of ZRX (not less after fees).
- /// @param orders An array of Order struct containing order specifications.
- /// @param zrxFillAmount A number representing the amount zrx to buy
- /// @return totalFillResults Expected fill result amounts from buying fees
- function calculateMarketBuyZrxResults(
- LibOrder.Order[] memory orders,
- uint256 zrxFillAmount
- )
- public
- view
- returns (FillResults memory totalFillResults)
- {
- for (uint256 i = 0; i < orders.length; i++) {
- uint256 remainingZrxFillAmount = safeSub(zrxFillAmount, totalFillResults.makerAssetFilledAmount);
- // Convert the remaining amount of makerToken to buy into remaining amount
- // of takerToken to sell, assuming entire amount can be sold in the current order
- uint256 remainingWethSellAmount = getPartialAmount(
- orders[i].takerAssetAmount,
- safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
- remainingZrxFillAmount
- );
- FillResults memory singleFillResult = calculateFillResults(orders[i], safeAdd(remainingWethSellAmount, 1));
-
- singleFillResult.makerAssetFilledAmount = safeSub(singleFillResult.makerAssetFilledAmount, singleFillResult.takerFeePaid);
- addFillResults(totalFillResults, singleFillResult);
- // As we compensate for the rounding issue above have slightly more ZRX than the requested zrxFillAmount
- if (totalFillResults.makerAssetFilledAmount >= zrxFillAmount) {
- break;
- }
- }
- return totalFillResults;
- }
-
- /// @dev Simulates the 0x Exchange fillOrder validation and calculations, without performing any state changes.
- /// @param order An Order struct containing order specifications.
- /// @param takerAssetFillAmount A number representing the amount of this order to fill.
- /// @return fillResults Amounts filled and fees paid by maker and taker.
- function calculateFillResults(
- LibOrder.Order memory order,
- uint256 takerAssetFillAmount
- )
- internal
- view
- returns (FillResults memory fillResults)
- {
- LibOrder.OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(order);
- if (orderInfo.orderStatus != uint8(LibOrder.OrderStatus.FILLABLE)) {
- return fillResults;
- }
- uint256 remainingTakerAssetAmount = safeSub(order.takerAssetAmount, orderInfo.orderTakerAssetFilledAmount);
- uint256 takerAssetFilledAmount = min256(takerAssetFillAmount, remainingTakerAssetAmount);
-
- fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
- fillResults.makerAssetFilledAmount = getPartialAmount(
- takerAssetFilledAmount,
- order.takerAssetAmount,
- order.makerAssetAmount
- );
- fillResults.makerFeePaid = getPartialAmount(
- takerAssetFilledAmount,
- order.takerAssetAmount,
- order.makerFee
- );
- fillResults.takerFeePaid = getPartialAmount(
- takerAssetFilledAmount,
- order.takerAssetAmount,
- order.takerFee
- );
- return fillResults;
- }
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinFees.sol b/packages/contracts/src/2.0.0/forwarder/MixinFees.sol
deleted file mode 100644
index 8ea00a1d5..000000000
--- a/packages/contracts/src/2.0.0/forwarder/MixinFees.sol
+++ /dev/null
@@ -1,126 +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 "../protocol/Exchange/libs/LibMath.sol";
-import "./mixins/MConstants.sol";
-import "./mixins/MFees.sol";
-
-
-contract MixinFees is
- LibMath,
- MConstants,
- MFees
-{
-
- uint16 constant public PERCENTAGE_DENOMINATOR = 10000; // 9800 == 98%, 10000 == 100%
- uint16 constant public MAX_FEE = 1000; // 10%
- uint16 constant public ALLOWABLE_EXCHANGE_PERCENTAGE = 9500; // 95%
-
- /// @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 Pays the feeRecipient feeProportion of the total takerEthAmount, denominated in ETH
- /// @param takerEthAmount The total amount that was transacted in WETH, fees are calculated from this value.
- /// @param feeProportion The proportion of fees
- /// @param feeRecipient The recipient of the fees
- /// @return ethFeeAmount Amount of ETH paid to feeRecipient as fee.
- function payEthFee(
- uint256 takerEthAmount,
- uint16 feeProportion,
- address feeRecipient
- )
- internal
- returns (uint256 ethFeeAmount)
- {
- if (feeProportion > 0 && feeRecipient != address(0)) {
- require(
- feeProportion <= MAX_FEE,
- "FEE_PROPORTION_TOO_LARGE"
- );
- // 1.5% is 150, allowing for 2 decimal precision, i.e 0.05% is 5
- ethFeeAmount = getPartialAmount(
- feeProportion,
- PERCENTAGE_DENOMINATOR,
- takerEthAmount
- );
- feeRecipient.transfer(ethFeeAmount);
- }
- return ethFeeAmount;
- }
-
- /// @dev Withdraws the remaining WETH, deduct and pay fees from this amount based on the takerTokenAmount to the feeRecipient.
- /// If a user overpaid ETH initially, the fees are calculated from the amount traded and deducted from withdrawAmount.
- /// Any remaining ETH is sent back to the user.
- /// @param ethWithdrawAmount The amount to withdraw from the WETH contract.
- /// @param wethAmountSold The total amount that was transacted in WETH, fees are calculated from this value.
- /// @param feeProportion The proportion of fees
- /// @param feeRecipient The recipient of the fees
- function withdrawPayAndDeductEthFee(
- uint256 ethWithdrawAmount,
- uint256 wethAmountSold,
- uint16 feeProportion,
- address feeRecipient
- )
- internal
- {
- // Return all of the excess WETH if any after deducting fees on the amount
- if (ethWithdrawAmount > 0) {
- ETHER_TOKEN.withdraw(ethWithdrawAmount);
- // Fees proportional to the amount traded
- uint256 ethFeeAmount = payEthFee(
- wethAmountSold,
- feeProportion,
- feeRecipient
- );
- uint256 unspentEthAmount = safeSub(ethWithdrawAmount, ethFeeAmount);
- if (unspentEthAmount > 0) {
- msg.sender.transfer(unspentEthAmount);
- }
- }
- }
-
- /// @dev Checks whether the amount of tokens sold against the amount of tokens requested
- /// is within a certain threshold. This ensures the caller gets a fair deal when
- /// performing any token fee abstraction. Threshold is 95%. If fee abstraction costs more than
- /// 5% of the total transaction, we return false.
- /// @param requestedSellAmount The amount the user requested, or sent in to a payable function
- /// @param tokenAmountSold The amount of the token that was sold after fee abstraction
- /// @return bool of whether this is within an acceptable threshold
- function isAcceptableThreshold(uint256 requestedSellAmount, uint256 tokenAmountSold)
- internal
- pure
- returns (bool)
- {
- uint256 acceptableSellAmount = getPartialAmount(
- ALLOWABLE_EXCHANGE_PERCENTAGE,
- PERCENTAGE_DENOMINATOR,
- requestedSellAmount
- );
- return tokenAmountSold >= acceptableSellAmount;
- }
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol
index eadeaf5ba..1164ae919 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol
@@ -19,30 +19,30 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../utils/LibBytes/LibBytes.sol";
-import "./mixins/MFees.sol";
-import "./mixins/MMarketBuyZrx.sol";
-import "./mixins/MExpectedResults.sol";
-import "./mixins/MTransfer.sol";
-import "./mixins/MConstants.sol";
+import "./libs/LibConstants.sol";
+import "./mixins/MWeth.sol";
+import "./mixins/MAssets.sol";
+import "./mixins/MExchangeWrapper.sol";
import "./mixins/MForwarderCore.sol";
+import "../utils/LibBytes/LibBytes.sol";
import "../protocol/Exchange/libs/LibOrder.sol";
import "../protocol/Exchange/libs/LibFillResults.sol";
+import "../protocol/Exchange/libs/LibMath.sol";
contract MixinForwarderCore is
LibFillResults,
- MConstants,
- MExpectedResults,
- MFees,
- MMarketBuyZrx,
- MTransfer,
+ LibMath,
+ LibConstants,
+ MWeth,
+ MAssets,
+ MExchangeWrapper,
MForwarderCore
{
- bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
- bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256,bytes)"));
- uint256 constant internal MAX_UINT = 2**256 - 1;
+ using LibBytes for bytes;
+
+ /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.
constructor ()
public
{
@@ -53,379 +53,202 @@ contract MixinForwarderCore is
}
}
- /// @dev Market sells ETH for ERC20 tokens, performing fee abstraction if required. This does not support ERC721 tokens. This function is payable
- /// and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// This function allows for a deduction of a proportion of incoming ETH sent to the feeRecipient.
- /// The caller is sent all tokens from the operation.
- /// If the purchased token amount does not meet an acceptable threshold then this function reverts.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the incoming ETH and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20(
+ /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (FillResults memory totalFillResults)
+ returns (
+ FillResults memory orderFillResults,
+ FillResults memory feeOrderFillResults
+ )
{
- uint256 takerEthAmount = msg.value;
- require(
- takerEthAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
- );
- // Deduct the fee from the total amount of ETH sent in
- uint256 ethFeeAmount = payEthFee(
- takerEthAmount,
- feeProportion,
- feeRecipient
- );
- uint256 wethSellAmount = safeSub(takerEthAmount, ethFeeAmount);
-
- // Deposit the remaining to be used for trading
- ETHER_TOKEN.deposit.value(wethSellAmount)();
- // Populate the known assetData, as it is always WETH the caller can provide null bytes to save gas
- // marketSellOrders fills the remaining
- address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16);
- orders[0].takerAssetData = WETH_ASSET_DATA;
- if (makerTokenAddress == address(ZRX_TOKEN)) {
- // If this is ZRX then we market sell from the orders, rather than a 2 step of buying ZRX fees from feeOrders
- // then buying ZRX from orders
- totalFillResults = marketSellEthForZRXInternal(
+ // Convert ETH to WETH.
+ convertEthToWeth();
+
+ uint256 wethSellAmount;
+ uint256 zrxBuyAmount;
+ uint256 makerAssetAmountPurchased;
+ if (orders[0].makerAssetData.equals(ZRX_ASSET_DATA)) {
+ // Calculate amount of WETH that won't be spent on ETH fees.
+ wethSellAmount = getPartialAmount(
+ PERCENTAGE_DENOMINATOR,
+ safeAdd(PERCENTAGE_DENOMINATOR, feePercentage),
+ msg.value
+ );
+ // Market sell available WETH.
+ // ZRX fees are paid with this contract's balance.
+ orderFillResults = marketSellWeth(
orders,
- signatures,
- wethSellAmount
+ wethSellAmount,
+ signatures
);
+ // The fee amount must be deducted from the amount transfered back to sender.
+ makerAssetAmountPurchased = safeSub(orderFillResults.makerAssetFilledAmount, orderFillResults.takerFeePaid);
} else {
- totalFillResults = marketSellEthForERC20Internal(
+ // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
+ wethSellAmount = getPartialAmount(
+ MAX_WETH_FILL_PERCENTAGE,
+ PERCENTAGE_DENOMINATOR,
+ msg.value
+ );
+ // Market sell 95% of WETH.
+ // ZRX fees are payed with this contract's balance.
+ orderFillResults = marketSellWeth(
orders,
- signatures,
+ wethSellAmount,
+ signatures
+ );
+ // Buy back all ZRX spent on fees.
+ zrxBuyAmount = orderFillResults.takerFeePaid;
+ feeOrderFillResults = marketBuyZrxWithWeth(
feeOrders,
- feeSignatures,
- wethSellAmount
+ zrxBuyAmount,
+ feeSignatures
);
+ makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
}
- // Prevent accidental WETH owned by this contract and it being spent
- require(
- takerEthAmount >= totalFillResults.takerAssetFilledAmount,
- "INVALID_MSG_VALUE"
- );
- // Ensure no WETH is left in this contract
- require(
- wethSellAmount == totalFillResults.takerAssetFilledAmount,
- "UNACCEPTABLE_THRESHOLD"
+
+ // Ensure that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold.
+ assertValidFillResults(
+ orderFillResults,
+ feeOrderFillResults,
+ zrxBuyAmount
);
- // Transfer all tokens to msg.sender
- transferERC20Token(
- makerTokenAddress,
- msg.sender,
- totalFillResults.makerAssetFilledAmount
+
+ // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
+ // Refund remaining ETH to msg.sender.
+ transferEthFeeAndRefund(
+ orderFillResults.takerAssetFilledAmount,
+ feeOrderFillResults.takerAssetFilledAmount,
+ feePercentage,
+ feeRecipient
);
- return totalFillResults;
+
+ // Transfer purchased assets to msg.sender.
+ transferPurchasedAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
}
- /// @dev Buys the exact amount of assets (ERC20 and ERC721), performing fee abstraction if required.
- /// All order assets must be of the same type. Deducts a proportional fee to fee recipient.
- /// This function is payable and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// The caller is sent all assets from the fill of orders. This function will revert unless the requested amount of assets are purchased.
- /// Any excess ETH sent will be returned to the caller
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param makerTokenFillAmount The amount of maker asset to buy.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the ETH spent and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketBuyTokensWithEth(
+ /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
+ uint256 makerAssetFillAmount,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (FillResults memory totalFillResults)
+ returns (
+ FillResults memory orderFillResults,
+ FillResults memory feeOrderFillResults
+ )
{
- uint256 takerEthAmount = msg.value;
- require(
- takerEthAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
- );
- require(
- makerTokenFillAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
- );
- bytes4 assetDataId = LibBytes.readBytes4(orders[0].makerAssetData, 0);
- require(
- assetDataId == ERC20_DATA_ID || assetDataId == ERC721_DATA_ID,
- "UNSUPPORTED_TOKEN_PROXY"
- );
-
- ETHER_TOKEN.deposit.value(takerEthAmount)();
- if (assetDataId == ERC20_DATA_ID) {
- totalFillResults = marketBuyERC20TokensInternal(
+ // 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 = marketBuyZrxWithWeth(
orders,
- signatures,
- feeOrders,
- feeSignatures,
- makerTokenFillAmount
+ makerAssetFillAmount,
+ signatures
);
- } else if (assetDataId == ERC721_DATA_ID) {
- totalFillResults = batchBuyERC721TokensInternal(
+ // 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 = marketBuyWithWeth(
orders,
- signatures,
+ makerAssetFillAmount,
+ signatures
+ );
+ // Buy back all ZRX spent on fees.
+ zrxBuyAmount = orderFillResults.takerFeePaid;
+ feeOrderFillResults = marketBuyZrxWithWeth(
feeOrders,
+ zrxBuyAmount,
feeSignatures
);
+ makerAssetAmountPurchased = orderFillResults.makerAssetFilledAmount;
}
- // Prevent accidental WETH owned by this contract and it being spent
- require(
- takerEthAmount >= totalFillResults.takerAssetFilledAmount,
- "INVALID_MSG_VALUE"
+
+ // Ensure that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold.
+ assertValidFillResults(
+ orderFillResults,
+ feeOrderFillResults,
+ zrxBuyAmount
);
- withdrawPayAndDeductEthFee(
- safeSub(takerEthAmount, totalFillResults.takerAssetFilledAmount),
- totalFillResults.takerAssetFilledAmount,
- feeProportion,
+
+ // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
+ // Refund remaining ETH to msg.sender.
+ transferEthFeeAndRefund(
+ orderFillResults.takerAssetFilledAmount,
+ feeOrderFillResults.takerAssetFilledAmount,
+ feePercentage,
feeRecipient
);
- return totalFillResults;
- }
- /// @dev Market sells WETH for ERC20 tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20Internal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 wethSellAmount
- )
- internal
- returns (FillResults memory totalFillResults)
- {
- uint256 remainingWethSellAmount = wethSellAmount;
- FillResults memory calculatedMarketSellResults = calculateMarketSellResults(orders, wethSellAmount);
- if (calculatedMarketSellResults.takerFeePaid > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future market buy
- FillResults memory feeTokensResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- calculatedMarketSellResults.takerFeePaid
- );
- // Ensure the token abstraction was fair if fees were proportionally too high, we fail
- require(
- isAcceptableThreshold(
- wethSellAmount,
- safeSub(wethSellAmount, feeTokensResults.takerAssetFilledAmount)
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- remainingWethSellAmount = safeSub(remainingWethSellAmount, feeTokensResults.takerAssetFilledAmount);
- totalFillResults.takerFeePaid = feeTokensResults.takerFeePaid;
- totalFillResults.takerAssetFilledAmount = feeTokensResults.takerAssetFilledAmount;
- }
- // Make our market sell to buy the requested tokens with the remaining balance
- FillResults memory requestedTokensResults = EXCHANGE.marketSellOrders(
- orders,
- remainingWethSellAmount,
- signatures
- );
- // Update our return FillResult with the market sell
- addFillResults(totalFillResults, requestedTokensResults);
- return totalFillResults;
+ // Transfer purchased assets to msg.sender.
+ transferPurchasedAssetToSender(orders[0].makerAssetData, makerAssetAmountPurchased);
}
- /// @dev Market sells WETH for ZRX tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForZRXInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 wethSellAmount
+ /// @dev Ensures that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold.
+ /// @param orderFillResults Amounts filled and fees paid for primary orders.
+ /// @param feeOrderFillResults Amounts filled and fees paid for fee orders.
+ /// @param zrxBuyAmount The amount of ZRX that needed to be repurchased after filling primary orders.
+ function assertValidFillResults(
+ FillResults memory orderFillResults,
+ FillResults memory feeOrderFillResults,
+ uint256 zrxBuyAmount
)
internal
- returns (FillResults memory totalFillResults)
+ view
{
- // Make our market sell to buy the requested tokens with the remaining balance
- totalFillResults = EXCHANGE.marketSellOrders(
- orders,
- wethSellAmount,
- signatures
- );
- // Exchange does not special case ZRX in the makerAssetFilledAmount, if fees were deducted then using this amount
- // for future transfers is invalid.
- uint256 zrxAmountBought = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
+ // Ensure that all ZRX spent while filling primary orders has been repurchased.
+ uint256 zrxPurchased = safeSub(feeOrderFillResults.makerAssetFilledAmount, feeOrderFillResults.takerFeePaid);
require(
- isAcceptableThreshold(totalFillResults.makerAssetFilledAmount, zrxAmountBought),
- "UNACCEPTABLE_THRESHOLD"
+ zrxPurchased >= zrxBuyAmount,
+ "COMPLETE_FILL_FAILED"
);
- totalFillResults.makerAssetFilledAmount = zrxAmountBought;
- return totalFillResults;
- }
- /// @dev Buys an exact amount of an ERC20 token using WETH.
- /// @param orders Orders to fill. The maker asset is the ERC20 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @param makerTokenFillAmount Amount of the ERC20 token to buy.
- /// @return totalFillResults Aggregated fill results of buying the ERC20 and ZRX tokens.
- function marketBuyERC20TokensInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults)
- {
- // We read the maker token address to check if it is ZRX and later use it for transfer
- address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16);
- // We assume that asset being bought by taker is the same for each order.
- // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
- orders[0].takerAssetData = WETH_ASSET_DATA;
- // We can short cut here for effeciency and use buyFeeTokensInternal if maker asset token is ZRX
- // this buys us exactly that amount taking into account the fees. This saves gas and calculates the rate correctly
- FillResults memory marketBuyResults;
- if (makerTokenAddress == address(ZRX_TOKEN)) {
- marketBuyResults = marketBuyZrxInternal(
- orders,
- signatures,
- makerTokenFillAmount
- );
- // When buying ZRX we round up which can result in a small margin excess
- require(
- marketBuyResults.makerAssetFilledAmount >= makerTokenFillAmount,
- "UNACCEPTABLE_THRESHOLD"
- );
- addFillResults(totalFillResults, marketBuyResults);
- require(
- isAcceptableThreshold(
- safeAdd(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid), // Total ZRX
- totalFillResults.makerAssetFilledAmount // amount going to msg.sender
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- } else {
- FillResults memory calculatedMarketBuyResults = calculateMarketBuyResults(orders, makerTokenFillAmount);
- if (calculatedMarketBuyResults.takerFeePaid > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future market buy
- FillResults memory zrxMarketBuyResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- calculatedMarketBuyResults.takerFeePaid
- );
- totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount;
- totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid;
- }
- // Make our market buy of the requested tokens with the remaining balance
- marketBuyResults = EXCHANGE.marketBuyOrders(
- orders,
- makerTokenFillAmount,
- signatures
- );
- require(
- marketBuyResults.makerAssetFilledAmount == makerTokenFillAmount,
- "UNACCEPTABLE_THRESHOLD"
- );
- addFillResults(totalFillResults, marketBuyResults);
- require(
- isAcceptableThreshold(
- totalFillResults.takerAssetFilledAmount,
- marketBuyResults.takerAssetFilledAmount
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- }
- // Transfer all purchased tokens to msg.sender
- transferERC20Token(
- makerTokenAddress,
- msg.sender,
- marketBuyResults.makerAssetFilledAmount
- );
- return totalFillResults;
- }
-
- /// @dev Buys an all of the ERC721 tokens in the orders.
- /// @param orders Orders to fill. The maker asset is the ERC721 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @return totalFillResults Aggregated fill results of buying the ERC721 tokens and ZRX tokens.
- function batchBuyERC721TokensInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults)
- {
- uint256 totalZrxFeeAmount;
- uint256 ordersLength = orders.length;
- uint256[] memory takerAssetFillAmounts = new uint256[](ordersLength);
- for (uint256 i = 0; i < ordersLength; i++) {
- // Total up the fees
- totalZrxFeeAmount = safeAdd(totalZrxFeeAmount, orders[i].takerFee);
- // We assume that asset being bought by taker is the same for each order.
- // Rather than passing this in as calldata, we set the takerAssetData as WETH asset data
- orders[i].takerAssetData = WETH_ASSET_DATA;
- // Populate takerAssetFillAmounts for later batchFill
- takerAssetFillAmounts[i] = orders[i].takerAssetAmount;
- }
- if (totalZrxFeeAmount > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future fill
- FillResults memory zrxMarketBuyResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- totalZrxFeeAmount
- );
- totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid;
- totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount;
- }
- FillResults memory batchFillResults = EXCHANGE.batchFillOrKillOrders(
- orders,
- takerAssetFillAmounts,
- signatures
- );
- addFillResults(totalFillResults, batchFillResults);
+ // Ensure that no extra WETH owned by this contract has been sold.
+ uint256 wethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount);
require(
- isAcceptableThreshold(
- totalFillResults.takerAssetFilledAmount,
- batchFillResults.takerAssetFilledAmount
- ),
- "UNACCEPTABLE_THRESHOLD"
+ wethSold <= msg.value,
+ "OVERSOLD_WETH"
);
- // Transfer all of the tokens filled from the batchFill
- for (i = 0; i < ordersLength; i++) {
- transferERC721Token(
- orders[i].makerAssetData,
- msg.sender
- );
- }
- return totalFillResults;
}
}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinMarketBuyZrx.sol b/packages/contracts/src/2.0.0/forwarder/MixinMarketBuyZrx.sol
deleted file mode 100644
index e272f8aad..000000000
--- a/packages/contracts/src/2.0.0/forwarder/MixinMarketBuyZrx.sol
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "../protocol/Exchange/libs/LibFillResults.sol";
-import "../protocol/Exchange/libs/LibOrder.sol";
-import "../protocol/Exchange/libs/LibMath.sol";
-import "./mixins/MConstants.sol";
-import "./mixins/MMarketBuyZrx.sol";
-
-
-contract MixinMarketBuyZrx is
- LibMath,
- LibFillResults,
- MConstants,
- MMarketBuyZrx
-{
-
- /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account the fees on buying fee tokens. This will guarantee
- /// At least zrxBuyAmount of ZRX fee tokens are purchased (sometimes slightly over due to rounding issues).
- /// It is possible that a request to buy 200 ZRX fee tokens will require purchasing 202 ZRX tokens
- /// As 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
- /// @param orders An array of Order struct containing order specifications for fees.
- /// @param signatures An array of Proof that order has been created by maker for the fee orders.
- /// @param zrxBuyAmount The number of requested ZRX fee tokens.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker. makerTokenAmount is the zrx amount deducted of fees
- function marketBuyZrxInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 zrxBuyAmount
- )
- internal
- returns (FillResults memory totalFillResults)
- {
- for (uint256 i = 0; i < orders.length; i++) {
- // All of these are ZRX/WETH, we can drop the respective assetData from callData
- orders[i].makerAssetData = ZRX_ASSET_DATA;
- orders[i].takerAssetData = WETH_ASSET_DATA;
- // Calculate the remaining amount of makerToken to buy
- uint256 remainingZrxBuyAmount = safeSub(zrxBuyAmount, totalFillResults.makerAssetFilledAmount);
- // Convert the remaining amount of makerToken to buy into remaining amount
- // of takerToken to sell, assuming entire amount can be sold in the current order
- uint256 remainingWethSellAmount = getPartialAmount(
- orders[i].takerAssetAmount,
- safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
- remainingZrxBuyAmount
- );
- // Attempt to sell the remaining amount of takerToken
- // Round up the amount to ensure we don't under buy by a fractional amount
- FillResults memory singleFillResult = EXCHANGE.fillOrder(
- orders[i],
- safeAdd(remainingWethSellAmount, 1),
- signatures[i]
- );
- // We didn't buy the full amount when buying ZRX as some were taken for fees
- singleFillResult.makerAssetFilledAmount = safeSub(singleFillResult.makerAssetFilledAmount, singleFillResult.takerFeePaid);
- // Update amounts filled and fees paid by maker and taker
- addFillResults(totalFillResults, singleFillResult);
- // Stop execution if the entire amount of makerToken has been bought
- if (totalFillResults.makerAssetFilledAmount >= zrxBuyAmount) {
- break;
- }
- }
- return totalFillResults;
- }
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol b/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol
new file mode 100644
index 000000000..8ba236e7f
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol
@@ -0,0 +1,110 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../protocol/Exchange/libs/LibMath.sol";
+import "./libs/LibConstants.sol";
+import "./mixins/MWeth.sol";
+
+
+contract MixinWeth is
+ LibMath,
+ LibConstants,
+ MWeth
+{
+
+ /// @dev Default payabale function, this allows us to withdraw WETH
+ function ()
+ public
+ payable
+ {
+ require(
+ msg.sender == address(ETHER_TOKEN),
+ "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"
+ );
+ }
+
+ /// @dev Converts message call's ETH value into WETH.
+ function convertEthToWeth()
+ internal
+ {
+ require(
+ msg.value > 0,
+ "INVALID_MSG_VALUE"
+ );
+ ETHER_TOKEN.deposit.value(msg.value)();
+ }
+
+ /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
+ /// Refunds any excess ETH to msg.sender.
+ /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.
+ /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ function transferEthFeeAndRefund(
+ uint256 wethSoldExcludingFeeOrders,
+ uint256 wethSoldForZrx,
+ uint256 feePercentage,
+ address feeRecipient
+ )
+ internal
+ {
+ // Ensure feePercentage is less than 5%.
+ require(
+ feePercentage <= MAX_FEE_PERCENTAGE,
+ "FEE_PERCENTAGE_TOO_LARGE"
+ );
+
+ // Calculate amount of WETH that hasn't been sold.
+ uint256 wethRemaining = safeSub(
+ msg.value,
+ safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx)
+ );
+
+ // Calculate ETH fee to pay to feeRecipient.
+ uint256 ethFee = getPartialAmount(
+ feePercentage,
+ PERCENTAGE_DENOMINATOR,
+ wethSoldExcludingFeeOrders
+ );
+
+ // Ensure fee is less than amount of WETH remaining.
+ require(
+ ethFee <= wethRemaining,
+ "INSUFFICIENT_ETH_REMAINING"
+ );
+
+ // Do nothing if no WETH remaining
+ if (wethRemaining > 0) {
+ // Convert remaining WETH to ETH
+ ETHER_TOKEN.withdraw(wethRemaining);
+
+ // Pay ETH to feeRecipient
+ if (ethFee > 0) {
+ feeRecipient.transfer(ethFee);
+ }
+
+ // Refund remaining ETH to msg.sender.
+ uint256 ethRefund = safeSub(wethRemaining, ethFee);
+ if (ethRefund > 0) {
+ msg.sender.transfer(ethRefund);
+ }
+ }
+ }
+}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MTransfer.sol b/packages/contracts/src/2.0.0/forwarder/interfaces/IAssets.sol
index 418a6288b..9b0d995eb 100644
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MTransfer.sol
+++ b/packages/contracts/src/2.0.0/forwarder/interfaces/IAssets.sol
@@ -19,28 +19,16 @@
pragma solidity 0.4.24;
-contract MTransfer {
-
- function onERC721Received(address, uint256, bytes memory)
- public
- pure
- returns(bytes4);
-
- function onERC721Received(address, address, uint256, bytes memory)
- public
- pure
- returns(bytes4);
-
- function transferERC20Token(
+contract IAssets {
+
+ /// @dev Withdraws ERC20 tokens 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 tokens that were accidentally sent to this contract.
+ /// @param token Address of ERC20 token to withdraw.
+ /// @param amount Amount of ERC20 token to withdraw.
+ function withdrawERC20(
address token,
- address to,
uint256 amount
)
- internal;
-
- function transferERC721Token(
- bytes memory assetData,
- address to
- )
- internal;
+ external;
}
diff --git a/packages/contracts/src/2.0.0/forwarder/interfaces/IExpectedResults.sol b/packages/contracts/src/2.0.0/forwarder/interfaces/IExpectedResults.sol
deleted file mode 100644
index 89187b750..000000000
--- a/packages/contracts/src/2.0.0/forwarder/interfaces/IExpectedResults.sol
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-
-
-contract IExpectedResults {
-
- /// @dev Calculates a total FillResults for buying makerAssetFillAmount over all orders.
- /// Including the fees required to be paid.
- /// @param orders An array of Order struct containing order specifications.
- /// @param makerAssetFillAmount A number representing the amount of this order to fill.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function calculateMarketBuyResults(
- LibOrder.Order[] memory orders,
- uint256 makerAssetFillAmount
- )
- public
- view
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Calculates a FillResults total for selling takerAssetFillAmount over all orders.
- /// Including the fees required to be paid.
- /// @param orders An array of Order struct containing order specifications.
- /// @param takerAssetFillAmount A number representing the amount of this order to fill.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker.
- function calculateMarketSellResults(
- LibOrder.Order[] memory orders,
- uint256 takerAssetFillAmount
- )
- public
- view
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Calculates fill results for buyFeeTokens. This handles fees on buying ZRX
- /// so the end result is the expected amount of ZRX (not less after fees).
- /// @param orders An array of Order struct containing order specifications.
- /// @param zrxFillAmount A number representing the amount zrx to buy
- /// @return totalFillResults Expected fill result amounts from buying fees
- function calculateMarketBuyZrxResults(
- LibOrder.Order[] memory orders,
- uint256 zrxFillAmount
- )
- public
- view
- returns (LibFillResults.FillResults memory totalFillResults);
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarder.sol b/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarder.sol
index 745dd29a9..f5a26e2ba 100644
--- a/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarder.sol
+++ b/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarder.sol
@@ -20,11 +20,11 @@ pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
import "./IForwarderCore.sol";
-import "./IExpectedResults.sol";
+import "./IAssets.sol";
// solhint-disable no-empty-blocks
contract IForwarder is
IForwarderCore,
- IExpectedResults
+ IAssets
{}
diff --git a/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarderCore.sol
index 7ac2a8af3..3ecbb133b 100644
--- a/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarderCore.sol
+++ b/packages/contracts/src/2.0.0/forwarder/interfaces/IForwarderCore.sol
@@ -25,55 +25,56 @@ import "../../protocol/Exchange/libs/LibFillResults.sol";
contract IForwarderCore {
- /// @dev Market sells ETH for ERC20 tokens, performing fee abstraction if required. This does not support ERC721 tokens. This function is payable
- /// and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// This function allows for a deduction of a proportion of incoming ETH sent to the feeRecipient.
- /// The caller is sent all tokens from the operation.
- /// If the purchased token amount does not meet an acceptable threshold then this function reverts.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the incoming ETH and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20(
+ /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (LibFillResults.FillResults memory totalFillResults);
+ returns (
+ LibFillResults.FillResults memory orderFillResults,
+ LibFillResults.FillResults memory feeOrderFillResults
+ );
- /// @dev Buys the exact amount of assets (ERC20 and ERC721), performing fee abstraction if required.
- /// All order assets must be of the same type. Deducts a proportional fee to fee recipient.
- /// This function is payable and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// The caller is sent all assets from the fill of orders. This function will revert unless the requested amount of assets are purchased.
- /// Any excess ETH sent will be returned to the caller
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param makerTokenFillAmount The amount of maker asset to buy.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the ETH spent and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketBuyTokensWithEth(
+ /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
+ uint256 makerAssetFillAmount,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (LibFillResults.FillResults memory totalFillResults);
+ returns (
+ LibFillResults.FillResults memory orderFillResults,
+ LibFillResults.FillResults memory feeOrderFillResults
+ );
}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol b/packages/contracts/src/2.0.0/forwarder/libs/LibConstants.sol
index 2b064d579..c26d7902c 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol
+++ b/packages/contracts/src/2.0.0/forwarder/libs/LibConstants.sol
@@ -18,12 +18,27 @@
pragma solidity 0.4.24;
-import "./mixins/MConstants.sol";
-
-
-contract MixinConstants is
- MConstants
-{
+import "../../protocol/Exchange/interfaces/IExchange.sol";
+import "../../tokens/EtherToken/IEtherToken.sol";
+import "../../tokens/ERC20Token/IERC20Token.sol";
+
+
+contract LibConstants {
+
+ 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,
diff --git a/packages/contracts/src/2.0.0/forwarder/libs/LibForwarderErrors.sol b/packages/contracts/src/2.0.0/forwarder/libs/LibForwarderErrors.sol
new file mode 100644
index 000000000..cdfb77a0b
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/libs/LibForwarderErrors.sol
@@ -0,0 +1,34 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+// solhint-disable
+pragma solidity 0.4.24;
+
+
+/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.
+contract LibForwarderErrors {
+ string constant FEE_PERCENTAGE_TOO_LARGE = "FEE_PROPORTION_TOO_LARGE"; // Provided fee percentage greater than 5%.
+ string constant INSUFFICIENT_ETH_REMAINING = "INSUFFICIENT_ETH_REMAINING"; // Not enough ETH remaining to pay feeRecipient.
+ string constant OVERSOLD_WETH = "OVERSOLD_WETH"; // More WETH sold than provided with current message call.
+ string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired purchase amount not completely filled (required for ZRX fees only).
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Asset transfer failed.
+ string constant UNSUPPORTED_TOKEN_PROXY = "UNSUPPORTED_TOKEN_PROXY"; // Proxy in assetData not supported.
+ string constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY = "DEFAULT_FUNCTION_WETH_CONTRACT_ONLY"; // Fallback function may only be used for WETH withdrawals.
+ string constant INVALID_MSG_VALUE = "INVALID_MSG_VALUE"; // msg.value must be greater than 0.
+ string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Amount must equal 1.
+}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol
new file mode 100644
index 000000000..340ee0bcb
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol
@@ -0,0 +1,54 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../interfaces/IAssets.sol";
+
+
+contract MAssets is
+ IAssets
+{
+
+ /// @dev Transfers given amount of asset to sender.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param amount Amount of asset to transfer to sender.
+ function transferPurchasedAssetToSender(
+ bytes memory assetData,
+ uint256 amount
+ )
+ internal;
+
+ /// @dev Decodes ERC20 assetData and transfers given amount to sender.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param amount Amount of asset to transfer to sender.
+ function transferERC20Token(
+ bytes memory assetData,
+ uint256 amount
+ )
+ internal;
+
+ /// @dev Decodes ERC721 assetData and transfers given amount to sender.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param amount Amount of asset to transfer to sender.
+ function transferERC721Token(
+ bytes memory assetData,
+ uint256 amount
+ )
+ internal;
+}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol
deleted file mode 100644
index 348bf169e..000000000
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol
+++ /dev/null
@@ -1,35 +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 "../../protocol/Exchange/interfaces/IExchange.sol";
-import "../../tokens/EtherToken/IEtherToken.sol";
-import "../../tokens/ERC20Token/IERC20Token.sol";
-
-
-contract MConstants {
-
- // 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
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol
new file mode 100644
index 000000000..5a2def7e5
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MExchangeWrapper.sol
@@ -0,0 +1,87 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../protocol/Exchange/libs/LibOrder.sol";
+import "../../protocol/Exchange/libs/LibFillResults.sol";
+
+
+contract MExchangeWrapper {
+
+ /// @dev Fills the input order.
+ /// Returns false if the transaction would otherwise revert.
+ /// @param order Order struct containing order specifications.
+ /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
+ /// @param signature Proof that order has been created by maker.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function fillOrderNoThrow(
+ LibOrder.Order memory order,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ internal
+ returns (LibFillResults.FillResults memory fillResults);
+
+ /// @dev Synchronously executes multiple calls of fillOrder until total amount of WETH has been sold by taker.
+ /// Returns false if the transaction would otherwise revert.
+ /// @param orders Array of order specifications.
+ /// @param wethSellAmount Desired amount of WETH to sell.
+ /// @param signatures Proofs that orders have been signed by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ function marketSellWeth(
+ LibOrder.Order[] memory orders,
+ uint256 wethSellAmount,
+ bytes[] memory signatures
+ )
+ internal
+ returns (LibFillResults.FillResults memory totalFillResults);
+
+ /// @dev Synchronously executes multiple fill orders in a single transaction until total amount is bought by taker.
+ /// Returns false if the transaction would otherwise revert.
+ /// The asset being sold by taker must always be WETH.
+ /// @param orders Array of order specifications.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
+ /// @param signatures Proofs that orders have been signed by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ function marketBuyWithWeth(
+ 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 marketBuyZrxWithWeth(
+ LibOrder.Order[] memory orders,
+ uint256 zrxBuyAmount,
+ bytes[] memory signatures
+ )
+ internal
+ returns (LibFillResults.FillResults memory totalFillResults);
+}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MExpectedResults.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MExpectedResults.sol
deleted file mode 100644
index cf03bb32e..000000000
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MExpectedResults.sol
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-pragma experimental ABIEncoderV2;
-
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-import "../interfaces/IExpectedResults.sol";
-
-
-contract MExpectedResults is
- IExpectedResults
-{
-
- /// @dev Simulates the 0x Exchange fillOrder validation and calculations, without performing any state changes.
- /// @param order An Order struct containing order specifications.
- /// @param takerAssetFillAmount A number representing the amount of this order to fill.
- /// @return fillResults Amounts filled and fees paid by maker and taker.
- function calculateFillResults(
- LibOrder.Order memory order,
- uint256 takerAssetFillAmount
- )
- internal
- view
- returns (LibFillResults.FillResults memory fillResults);
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MFees.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MFees.sol
deleted file mode 100644
index f332637ea..000000000
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MFees.sol
+++ /dev/null
@@ -1,63 +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 MFees {
-
- /// @dev Pays the feeRecipient feeProportion of the total takerEthAmount, denominated in ETH
- /// @param takerEthAmount The total amount that was transacted in WETH, fees are calculated from this value.
- /// @param feeProportion The proportion of fees
- /// @param feeRecipient The recipient of the fees
- /// @return ethFeeAmount Amount of ETH paid to feeRecipient as fee.
- function payEthFee(
- uint256 takerEthAmount,
- uint16 feeProportion,
- address feeRecipient
- )
- internal
- returns (uint256 ethFeeAmount);
-
- /// @dev Withdraws the remaining WETH, deduct and pay fees from this amount based on the takerTokenAmount to the feeRecipient.
- /// If a user overpaid ETH initially, the fees are calculated from the amount traded and deducted from withdrawAmount.
- /// Any remaining ETH is sent back to the user.
- /// @param ethWithdrawAmount The amount to withdraw from the WETH contract.
- /// @param wethAmountSold The total amount that was transacted in WETH, fees are calculated from this value.
- /// @param feeProportion The proportion of fees
- /// @param feeRecipient The recipient of the fees
- function withdrawPayAndDeductEthFee(
- uint256 ethWithdrawAmount,
- uint256 wethAmountSold,
- uint16 feeProportion,
- address feeRecipient
- )
- internal;
-
- /// @dev Checks whether the amount of tokens sold against the amount of tokens requested
- /// is within a certain threshold. This ensures the caller gets a fair deal when
- /// performing any token fee abstraction. Threshold is 95%. If fee abstraction costs more than
- /// 5% of the total transaction, we return false.
- /// @param requestedSellAmount The amount the user requested, or sent in to a payable function
- /// @param tokenAmountSold The amount of the token that was sold after fee abstraction
- /// @return bool of whether this is within an acceptable threshold
- function isAcceptableThreshold(uint256 requestedSellAmount, uint256 tokenAmountSold)
- internal
- pure
- returns (bool);
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol
index 4a54e76b1..0f5cd9c66 100644
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol
@@ -28,65 +28,15 @@ contract MForwarderCore is
IForwarderCore
{
- /// @dev Market sells WETH for ERC20 tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20Internal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 wethSellAmount
+ /// @dev Ensures that all ZRX fees have been repurchased and no extra WETH owned by this contract has been sold.
+ /// @param orderFillResults Amounts filled and fees paid for primary orders.
+ /// @param feeOrderFillResults Amounts filled and fees paid for fee orders.
+ /// @param zrxBuyAmount The amount of ZRX that needed to be repurchased after filling primary orders.
+ function assertValidFillResults(
+ LibFillResults.FillResults memory orderFillResults,
+ LibFillResults.FillResults memory feeOrderFillResults,
+ uint256 zrxBuyAmount
)
internal
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Market sells WETH for ZRX tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForZRXInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 wethSellAmount
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Buys an exact amount of an ERC20 token using WETH.
- /// @param orders Orders to fill. The maker asset is the ERC20 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @param makerTokenFillAmount Amount of the ERC20 token to buy.
- /// @return totalFillResults Aggregated fill results of buying the ERC20 and ZRX tokens.
- function marketBuyERC20TokensInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-
- /// @dev Buys an all of the ERC721 tokens in the orders.
- /// @param orders Orders to fill. The maker asset is the ERC721 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @return totalFillResults Aggregated fill results of buying the ERC721 tokens and ZRX tokens.
- function batchBuyERC721TokensInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
+ view;
}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MMarketBuyZrx.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MMarketBuyZrx.sol
deleted file mode 100644
index 3501ef001..000000000
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MMarketBuyZrx.sol
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "../../protocol/Exchange/libs/LibFillResults.sol";
-import "../../protocol/Exchange/libs/LibOrder.sol";
-
-
-contract MMarketBuyZrx {
-
- /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account the fees on buying fee tokens. This will guarantee
- /// At least zrxBuyAmount of ZRX fee tokens are purchased (sometimes slightly over due to rounding issues).
- /// It is possible that a request to buy 200 ZRX fee tokens will require purchasing 202 ZRX tokens
- /// As 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
- /// @param orders An array of Order struct containing order specifications for fees.
- /// @param signatures An array of Proof that order has been created by maker for the fee orders.
- /// @param zrxBuyAmount The number of requested ZRX fee tokens.
- /// @return totalFillResults Amounts filled and fees paid by maker and taker. makerTokenAmount is the zrx amount deducted of fees
- function marketBuyZrxInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 zrxBuyAmount
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults);
-}
diff --git a/packages/contracts/src/2.0.0/forwarder/mixins/MWeth.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MWeth.sol
new file mode 100644
index 000000000..88e77be4e
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MWeth.sol
@@ -0,0 +1,41 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+
+contract MWeth {
+
+ /// @dev Converts message call's ETH value into WETH.
+ function convertEthToWeth()
+ internal;
+
+ /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
+ /// Refunds any excess ETH to msg.sender.
+ /// @param wethSoldExcludingFeeOrders Amount of WETH sold when filling primary orders.
+ /// @param wethSoldForZrx Amount of WETH sold when purchasing ZRX required for primary order fees.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ function transferEthFeeAndRefund(
+ uint256 wethSoldExcludingFeeOrders,
+ uint256 wethSoldForZrx,
+ uint256 feePercentage,
+ address feeRecipient
+ )
+ internal;
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
index 1f9958b43..6a70c9f60 100644
--- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
@@ -26,7 +26,7 @@ contract ERC721Proxy is
MixinAuthorizable
{
// Id of this proxy.
- bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256,bytes)"));
+ bytes4 constant internal PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
// solhint-disable-next-line payable-fallback
function ()
@@ -83,34 +83,26 @@ contract ERC721Proxy is
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.
-
+
// Asset data itself is encoded as follows:
//
// | Area | Offset | Length | Contents |
// |----------|--------|---------|-------------------------------------|
// | Header | 0 | 4 | function selector |
- // | Params | | 3 * 32 | function parameters: |
+ // | Params | | 2 * 32 | function parameters: |
// | | 4 | 12 + 20 | 1. token address |
// | | 36 | | 2. tokenId |
- // | | 68 | | 3. offset to receiverData (*) |
- // | Data | | | receiverData: |
- // | | 100 | 32 | receiverData Length |
- // | | 132 | ** | receiverData Contents |
- // We construct calldata for the `token.safeTransferFrom` ABI.
+ // We construct calldata for the `token.transferFrom` ABI.
// The layout of this calldata is in the table below.
//
// | Area | Offset | Length | Contents |
// |----------|--------|---------|-------------------------------------|
// | Header | 0 | 4 | function selector |
- // | Params | | 4 * 32 | function parameters: |
+ // | Params | | 3 * 32 | function parameters: |
// | | 4 | | 1. from |
// | | 36 | | 2. to |
// | | 68 | | 3. tokenId |
- // | | 100 | | 4. offset to receiverData (*) |
- // | Data | | | receiverData: |
- // | | 132 | 32 | receiverData Length |
- // | | 164 | ** | receiverData Contents |
// There exists only 1 of each token.
// require(amount == 1, "INVALID_AMOUNT")
@@ -122,76 +114,32 @@ contract ERC721Proxy is
mstore(96, 0)
revert(0, 100)
}
-
- // Require assetData to be at least 132 bytes
- let offset := calldataload(4)
- if lt(calldataload(add(offset, 4)), 132) {
- // Revert with `Error("LENGTH_GREATER_THAN_131_REQUIRED")`
- mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
- mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
- mstore(64, 0x000000204c454e4754485f475245415445525f5448414e5f3133315f52455155)
- mstore(96, 0x4952454400000000000000000000000000000000000000000000000000000000)
- revert(0, 100)
- }
-
- /////// Setup State ///////
- // `cdStart` is the start of the calldata for
- // `token.safeTransferFrom` (equal to free memory ptr).
- let cdStart := mload(64)
- // `dataAreaLength` is the total number of words
- // needed to store `receiverData`
- // As-per the ABI spec, this value is padded up to
- // the nearest multiple of 32,
- // and includes 32-bytes for length.
- // It's calculated as folows:
- // - Unpadded length in bytes = `mload(receiverData) + 32`
- // - Add 31 to convert rounding down to rounding up.
- // Combined with the previous and this is `63`.
- // - Round down to nearest multiple of 32 by clearing
- // bits 0x1F. This is done with `and` and a mask.
/////// Setup Header Area ///////
- // This area holds the 4-byte `transferFromSelector`.
+ // This area holds the 4-byte `transferFrom` selector.
// Any trailing data in transferFromSelector will be
// overwritten in the next `mstore` call.
- mstore(cdStart, 0xb88d4fde00000000000000000000000000000000000000000000000000000000)
+ mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
/////// Setup Params Area ///////
- // Each parameter is padded to 32-bytes.
- // The entire Params Area is 128 bytes.
- // Notes:
- // 1. A 20-byte mask is applied to addresses
- // to zero-out the unused bytes.
- // 2. The offset to `receiverData` is the length
- // of the Params Area (128 bytes).
-
- let length := calldataload(add(offset, 136))
- let token := calldataload(add(offset, 40))
-
- // Round length up to multiple of 32
- length := and(add(length, 31), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0)
-
- // Copy `from` and `to`
- calldatacopy(add(cdStart, 4), 36, 64)
-
- // TokenId
- mstore(add(cdStart, 68), calldataload(add(offset, 72)))
-
- // Offset to receiverData
- mstore(add(cdStart, 100), 128)
-
- // receiverData (including length)
- calldatacopy(add(cdStart, 132), add(offset, 136), add(length, 32))
-
- /////// Call `token.safeTransferFrom` using the calldata ///////
+ // We copy the fields `from` and `to` in bulk
+ // from our own calldata to the new calldata.
+ calldatacopy(4, 36, 64)
+
+ // Copy `tokenId` field from our own calldata to the new calldata.
+ let assetDataOffset := calldataload(4)
+ calldatacopy(68, add(assetDataOffset, 72), 32)
+
+ /////// Call `token.transferFrom` using the calldata ///////
+ let token := calldataload(add(assetDataOffset, 40))
let success := call(
- gas, // forward all gas
- token, // call address of token contract
- 0, // don't send any ETH
- cdStart, // pointer to start of input
- add(length, 164), // length of input
- 0, // write output to null
- 0 // output size is 0 bytes
+ gas, // forward all gas
+ token, // call address of token contract
+ 0, // don't send any ETH
+ 0, // pointer to start of input
+ 100, // length of input
+ 0, // write output to null
+ 0 // output size is 0 bytes
)
if success {
return(0, 0)
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
index 6f435892b..354d0e9c3 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
@@ -46,7 +46,7 @@ contract MixinExchangeCore is
mapping (bytes32 => bool) public cancelled;
// Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
- // Orders with specified senderAddress and with a salt less than their epoch to are considered cancelled
+ // Orders with specified senderAddress and with a salt less than their epoch are considered cancelled
mapping (address => mapping (address => uint256)) public orderEpoch;
/// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
index d420f7e85..86194f461 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
@@ -22,14 +22,17 @@ pragma experimental ABIEncoderV2;
import "./libs/LibMath.sol";
import "./libs/LibOrder.sol";
import "./libs/LibFillResults.sol";
+import "./libs/LibAbiEncoder.sol";
import "./mixins/MExchangeCore.sol";
contract MixinWrapperFunctions is
LibMath,
LibFillResults,
+ LibAbiEncoder,
MExchangeCore
{
+
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
@@ -54,7 +57,7 @@ contract MixinWrapperFunctions is
return fillResults;
}
- /// @dev Fills an order with specified parameters and ECDSA signature.
+ /// @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.
@@ -68,177 +71,21 @@ contract MixinWrapperFunctions is
public
returns (FillResults memory fillResults)
{
- // We need to call MExchangeCore.fillOrder using a delegatecall in
- // assembly so that we can intercept a call that throws. For this, we
- // need the input encoded in memory in the Ethereum ABIv2 format [1].
-
- // | Area | Offset | Length | Contents |
- // | -------- |--------|---------|-------------------------------------------- |
- // | Header | 0x00 | 4 | function selector |
- // | Params | | 3 * 32 | function parameters: |
- // | | 0x00 | | 1. offset to order (*) |
- // | | 0x20 | | 2. takerAssetFillAmount |
- // | | 0x40 | | 3. offset to signature (*) |
- // | Data | | 12 * 32 | order: |
- // | | 0x000 | | 1. senderAddress |
- // | | 0x020 | | 2. makerAddress |
- // | | 0x040 | | 3. takerAddress |
- // | | 0x060 | | 4. feeRecipientAddress |
- // | | 0x080 | | 5. makerAssetAmount |
- // | | 0x0A0 | | 6. takerAssetAmount |
- // | | 0x0C0 | | 7. makerFeeAmount |
- // | | 0x0E0 | | 8. takerFeeAmount |
- // | | 0x100 | | 9. expirationTimeSeconds |
- // | | 0x120 | | 10. salt |
- // | | 0x140 | | 11. Offset to makerAssetData (*) |
- // | | 0x160 | | 12. Offset to takerAssetData (*) |
- // | | 0x180 | 32 | makerAssetData Length |
- // | | 0x1A0 | ** | makerAssetData Contents |
- // | | 0x1C0 | 32 | takerAssetData Length |
- // | | 0x1E0 | ** | takerAssetData Contents |
- // | | 0x200 | 32 | signature Length |
- // | | 0x220 | ** | signature Contents |
-
- // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
- // An offset stored in the Params area is calculated from the beginning of the Params section.
- // An offset stored in the Data area is calculated from the beginning of the Data section.
-
- // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
-
- // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
-
- bytes4 fillOrderSelector = this.fillOrder.selector;
+ // ABI encode calldata for `fillOrder`
+ bytes memory fillOrderCalldata = abiEncodeFillOrder(
+ order,
+ takerAssetFillAmount,
+ signature
+ );
+ // Delegate to `fillOrder` and handle any exceptions gracefully
assembly {
-
- // Areas below may use the following variables:
- // 1. <area>Start -- Start of this area in memory
- // 2. <area>End -- End of this area in memory. This value may
- // be precomputed (before writing contents),
- // or it may be computed as contents are written.
- // 3. <area>Offset -- Current offset into area. If an area's End
- // is precomputed, this variable tracks the
- // offsets of contents as they are written.
-
- /////// Setup Header Area ///////
- // Load free memory pointer
- let headerAreaStart := mload(0x40)
- mstore(headerAreaStart, fillOrderSelector)
- let headerAreaEnd := add(headerAreaStart, 0x4)
-
- /////// Setup Params Area ///////
- // This area is preallocated and written to later.
- // This is because we need to fill in offsets that have not yet been calculated.
- let paramsAreaStart := headerAreaEnd
- let paramsAreaEnd := add(paramsAreaStart, 0x60)
- let paramsAreaOffset := paramsAreaStart
-
- /////// Setup Data Area ///////
- let dataAreaStart := paramsAreaEnd
- let dataAreaEnd := dataAreaStart
-
- // Offset from the source data we're reading from
- let sourceOffset := order
- // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
- let arrayLenBytes := 0
- let arrayLenWords := 0
-
- /////// Write order Struct ///////
- // Write memory location of Order, relative to the start of the
- // parameter list, then increment the paramsAreaOffset respectively.
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
- paramsAreaOffset := add(paramsAreaOffset, 0x20)
-
- // Write values for each field in the order
- // It would be nice to use a loop, but we save on gas by writing
- // the stores sequentially.
- mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress
- mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress
- mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress
- mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress
- mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount
- mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount
- mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount
- mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
- mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
- mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
- mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData
- mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
- dataAreaEnd := add(dataAreaEnd, 0x180)
- sourceOffset := add(sourceOffset, 0x180)
-
- // Write offset to <order.makerAssetData>
- mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
-
- // Calculate length of <order.makerAssetData>
- sourceOffset := mload(add(order, 0x140)) // makerAssetData
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of <order.makerAssetData>
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of <order.makerAssetData>
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- // Write offset to <order.takerAssetData>
- mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
-
- // Calculate length of <order.takerAssetData>
- sourceOffset := mload(add(order, 0x160)) // takerAssetData
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of <order.takerAssetData>
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of <order.takerAssetData>
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- /////// Write takerAssetFillAmount ///////
- mstore(paramsAreaOffset, takerAssetFillAmount)
- paramsAreaOffset := add(paramsAreaOffset, 0x20)
-
- /////// Write signature ///////
- // Write offset to paramsArea
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
-
- // Calculate length of signature
- sourceOffset := signature
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of signature
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of signature
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- // Execute delegatecall
let success := delegatecall(
gas, // forward all gas, TODO: look into gas consumption of assert/throw
address, // call address of this contract
- headerAreaStart, // pointer to start of input
- sub(dataAreaEnd, headerAreaStart), // length of input
- headerAreaStart, // write output over input
+ add(fillOrderCalldata, 32), // pointer to start of input (skip array length in first 32 bytes)
+ mload(fillOrderCalldata), // length of input
+ fillOrderCalldata, // write output over input
128 // output size is 128 bytes
)
switch success
@@ -249,10 +96,10 @@ contract MixinWrapperFunctions is
mstore(add(fillResults, 96), 0)
}
case 1 {
- mstore(fillResults, mload(headerAreaStart))
- mstore(add(fillResults, 32), mload(add(headerAreaStart, 32)))
- mstore(add(fillResults, 64), mload(add(headerAreaStart, 64)))
- mstore(add(fillResults, 96), mload(add(headerAreaStart, 96)))
+ mstore(fillResults, mload(fillOrderCalldata))
+ mstore(add(fillResults, 32), mload(add(fillOrderCalldata, 32)))
+ mstore(add(fillResults, 64), mload(add(fillOrderCalldata, 64)))
+ mstore(add(fillResults, 96), mload(add(fillOrderCalldata, 96)))
}
}
return fillResults;
@@ -272,7 +119,8 @@ contract MixinWrapperFunctions is
public
returns (FillResults memory totalFillResults)
{
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
FillResults memory singleFillResults = fillOrder(
orders[i],
takerAssetFillAmounts[i],
@@ -297,7 +145,8 @@ contract MixinWrapperFunctions is
public
returns (FillResults memory totalFillResults)
{
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
FillResults memory singleFillResults = fillOrKillOrder(
orders[i],
takerAssetFillAmounts[i],
@@ -323,7 +172,8 @@ contract MixinWrapperFunctions is
public
returns (FillResults memory totalFillResults)
{
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
FillResults memory singleFillResults = fillOrderNoThrow(
orders[i],
takerAssetFillAmounts[i],
@@ -349,7 +199,8 @@ contract MixinWrapperFunctions is
{
bytes memory takerAssetData = orders[0].takerAssetData;
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
// We assume that asset being sold by taker is the same for each order.
// Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
@@ -369,7 +220,7 @@ contract MixinWrapperFunctions is
addFillResults(totalFillResults, singleFillResults);
// Stop execution if the entire amount of takerAsset has been sold
- if (totalFillResults.takerAssetFilledAmount == takerAssetFillAmount) {
+ if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
break;
}
}
@@ -392,7 +243,8 @@ contract MixinWrapperFunctions is
{
bytes memory takerAssetData = orders[0].takerAssetData;
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
// We assume that asset being sold by taker is the same for each order.
// Rather than passing this in as calldata, we use the takerAssetData from the first order in all later orders.
@@ -412,7 +264,7 @@ contract MixinWrapperFunctions is
addFillResults(totalFillResults, singleFillResults);
// Stop execution if the entire amount of takerAsset has been sold
- if (totalFillResults.takerAssetFilledAmount == takerAssetFillAmount) {
+ if (totalFillResults.takerAssetFilledAmount >= takerAssetFillAmount) {
break;
}
}
@@ -434,7 +286,8 @@ contract MixinWrapperFunctions is
{
bytes memory makerAssetData = orders[0].makerAssetData;
- for (uint256 i = 0; i < orders.length; i++) {
+ 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.
// Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
@@ -462,7 +315,7 @@ contract MixinWrapperFunctions is
addFillResults(totalFillResults, singleFillResults);
// Stop execution if the entire amount of makerAsset has been bought
- if (totalFillResults.makerAssetFilledAmount == makerAssetFillAmount) {
+ if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
break;
}
}
@@ -485,7 +338,8 @@ contract MixinWrapperFunctions is
{
bytes memory makerAssetData = orders[0].makerAssetData;
- for (uint256 i = 0; i < orders.length; i++) {
+ 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.
// Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
@@ -513,7 +367,7 @@ contract MixinWrapperFunctions is
addFillResults(totalFillResults, singleFillResults);
// Stop execution if the entire amount of makerAsset has been bought
- if (totalFillResults.makerAssetFilledAmount == makerAssetFillAmount) {
+ if (totalFillResults.makerAssetFilledAmount >= makerAssetFillAmount) {
break;
}
}
@@ -525,7 +379,8 @@ contract MixinWrapperFunctions is
function batchCancelOrders(LibOrder.Order[] memory orders)
public
{
- for (uint256 i = 0; i < orders.length; i++) {
+ uint256 ordersLength = orders.length;
+ for (uint256 i = 0; i != ordersLength; i++) {
cancelOrder(orders[i]);
}
}
@@ -538,9 +393,9 @@ contract MixinWrapperFunctions is
view
returns (LibOrder.OrderInfo[] memory)
{
- uint256 length = orders.length;
- LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](length);
- for (uint256 i = 0; i < length; i++) {
+ uint256 ordersLength = orders.length;
+ LibOrder.OrderInfo[] memory ordersInfo = new LibOrder.OrderInfo[](ordersLength);
+ for (uint256 i = 0; i != ordersLength; i++) {
ordersInfo[i] = getOrderInfo(orders[i]);
}
return ordersInfo;
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol
new file mode 100644
index 000000000..704c7061c
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibAbiEncoder.sol
@@ -0,0 +1,218 @@
+/*
+
+ 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 "./LibOrder.sol";
+
+
+contract LibAbiEncoder {
+
+ /// @dev ABI encodes calldata for `fillOrder` in memory and returns the address range.
+ /// This range can be passed into `call` or `delegatecall` to invoke an external
+ /// call to `fillOrder`.
+ /// @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 calldataBegin Memory address of ABI encoded calldata.
+ /// @return calldataLength Lenfgth of ABI encoded calldata.
+ function abiEncodeFillOrder(
+ LibOrder.Order memory order,
+ uint256 takerAssetFillAmount,
+ bytes memory signature
+ )
+ public
+ pure
+ returns (bytes memory fillOrderCalldata)
+ {
+ // We need to call MExchangeCore.fillOrder using a delegatecall in
+ // assembly so that we can intercept a call that throws. For this, we
+ // need the input encoded in memory in the Ethereum ABIv2 format [1].
+
+ // | Area | Offset | Length | Contents |
+ // | -------- |--------|---------|-------------------------------------------- |
+ // | Header | 0x00 | 4 | function selector |
+ // | Params | | 3 * 32 | function parameters: |
+ // | | 0x00 | | 1. offset to order (*) |
+ // | | 0x20 | | 2. takerAssetFillAmount |
+ // | | 0x40 | | 3. offset to signature (*) |
+ // | Data | | 12 * 32 | order: |
+ // | | 0x000 | | 1. senderAddress |
+ // | | 0x020 | | 2. makerAddress |
+ // | | 0x040 | | 3. takerAddress |
+ // | | 0x060 | | 4. feeRecipientAddress |
+ // | | 0x080 | | 5. makerAssetAmount |
+ // | | 0x0A0 | | 6. takerAssetAmount |
+ // | | 0x0C0 | | 7. makerFeeAmount |
+ // | | 0x0E0 | | 8. takerFeeAmount |
+ // | | 0x100 | | 9. expirationTimeSeconds |
+ // | | 0x120 | | 10. salt |
+ // | | 0x140 | | 11. Offset to makerAssetData (*) |
+ // | | 0x160 | | 12. Offset to takerAssetData (*) |
+ // | | 0x180 | 32 | makerAssetData Length |
+ // | | 0x1A0 | ** | makerAssetData Contents |
+ // | | 0x1C0 | 32 | takerAssetData Length |
+ // | | 0x1E0 | ** | takerAssetData Contents |
+ // | | 0x200 | 32 | signature Length |
+ // | | 0x220 | ** | signature Contents |
+
+ // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
+ // An offset stored in the Params area is calculated from the beginning of the Params section.
+ // An offset stored in the Data area is calculated from the beginning of the Data section.
+
+ // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
+
+ // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
+
+ assembly {
+
+ // Areas below may use the following variables:
+ // 1. <area>Start -- Start of this area in memory
+ // 2. <area>End -- End of this area in memory. This value may
+ // be precomputed (before writing contents),
+ // or it may be computed as contents are written.
+ // 3. <area>Offset -- Current offset into area. If an area's End
+ // is precomputed, this variable tracks the
+ // offsets of contents as they are written.
+
+ /////// Setup Header Area ///////
+ // Load free memory pointer
+ fillOrderCalldata := mload(0x40)
+ // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"))
+ // = 0xb4be83d5
+ // Leave 0x20 bytes to store the length
+ mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)
+ let headerAreaEnd := add(fillOrderCalldata, 0x24)
+
+ /////// Setup Params Area ///////
+ // This area is preallocated and written to later.
+ // This is because we need to fill in offsets that have not yet been calculated.
+ let paramsAreaStart := headerAreaEnd
+ let paramsAreaEnd := add(paramsAreaStart, 0x60)
+ let paramsAreaOffset := paramsAreaStart
+
+ /////// Setup Data Area ///////
+ let dataAreaStart := paramsAreaEnd
+ let dataAreaEnd := dataAreaStart
+
+ // Offset from the source data we're reading from
+ let sourceOffset := order
+ // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
+ let arrayLenBytes := 0
+ let arrayLenWords := 0
+
+ /////// Write order Struct ///////
+ // Write memory location of Order, relative to the start of the
+ // parameter list, then increment the paramsAreaOffset respectively.
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ // Write values for each field in the order
+ // It would be nice to use a loop, but we save on gas by writing
+ // the stores sequentially.
+ mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress
+ mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress
+ mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress
+ mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress
+ mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount
+ mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount
+ mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount
+ mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
+ mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
+ mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
+ mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData
+ mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
+ dataAreaEnd := add(dataAreaEnd, 0x180)
+ sourceOffset := add(sourceOffset, 0x180)
+
+ // Write offset to <order.makerAssetData>
+ mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.makerAssetData>
+ sourceOffset := mload(add(order, 0x140)) // makerAssetData
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of <order.makerAssetData>
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.makerAssetData>
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Write offset to <order.takerAssetData>
+ mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.takerAssetData>
+ sourceOffset := mload(add(order, 0x160)) // takerAssetData
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of <order.takerAssetData>
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.takerAssetData>
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ /////// Write takerAssetFillAmount ///////
+ mstore(paramsAreaOffset, takerAssetFillAmount)
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ /////// Write signature ///////
+ // Write offset to paramsArea
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+
+ // Calculate length of signature
+ sourceOffset := signature
+ arrayLenBytes := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
+
+ // Write length of signature
+ mstore(dataAreaEnd, arrayLenBytes)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of signature
+ for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Set length of calldata
+ mstore(
+ fillOrderCalldata,
+ sub(dataAreaEnd, add(fillOrderCalldata, 0x20))
+ )
+ }
+
+ return fillOrderCalldata;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol
index 6918d755e..8d2732cd3 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibConstants.sol
@@ -19,11 +19,23 @@
pragma solidity 0.4.24;
+// solhint-disable max-line-length
contract LibConstants {
// Asset data for ZRX token. Used for fee transfers.
// @TODO: Hardcode constant when we deploy. Currently
// not constant to make testing easier.
+
+ // The proxyId for ZRX_ASSET_DATA is bytes4(keccak256("ERC20Token(address)")) = 0xf47261b0
+
+ // Kovan ZRX address is 0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570.
+ // The ABI encoded proxyId and address is 0xf47261b00000000000000000000000006ff6c0ff1d68b964901f986d4c9fa3ac68346570
+ // bytes constant public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6f\xf6\xc0\xff\x1d\x68\xb9\x64\x90\x1f\x98\x6d\x4c\x9f\xa3\xac\x68\x34\x65\x70";
+
+ // Mainnet ZRX address is 0xe41d2489571d322189246dafa5ebde1f4699f498.
+ // The ABI encoded proxyId and address is 0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498
+ // bytes constant public ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe4\x1d\x24\x89\x57\x1d\x32\x21\x89\x24\x6d\xaf\xa5\xeb\xde\x1f\x46\x99\xf4\x98";
+
// solhint-disable-next-line var-name-mixedcase
bytes public ZRX_ASSET_DATA;
@@ -34,3 +46,4 @@ contract LibConstants {
ZRX_ASSET_DATA = zrxAssetData;
}
}
+// solhint-enable max-line-length
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
index 46c13d390..fa09da6ac 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
@@ -33,7 +33,8 @@ contract LibMath is
function getPartialAmount(
uint256 numerator,
uint256 denominator,
- uint256 target)
+ uint256 target
+ )
internal
pure
returns (uint256 partialAmount)
@@ -53,7 +54,8 @@ contract LibMath is
function isRoundingError(
uint256 numerator,
uint256 denominator,
- uint256 target)
+ uint256 target
+ )
internal
pure
returns (bool isError)
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol
index 68f2c8aed..4031ff26b 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibOrder.sol
@@ -103,20 +103,20 @@ contract LibOrder is
bytes32 takerAssetDataHash = keccak256(order.takerAssetData);
// Assembly for more efficiently computing:
- // keccak256(abi.encode(
- // order.makerAddress,
- // order.takerAddress,
- // order.feeRecipientAddress,
- // order.senderAddress,
- // order.makerAssetAmount,
- // order.takerAssetAmount,
- // order.makerFee,
- // order.takerFee,
- // order.expirationTimeSeconds,
- // order.salt,
- // keccak256(order.makerAssetData),
- // keccak256(order.takerAssetData)
- // ));
+ // keccak256(abi.encode(
+ // order.makerAddress,
+ // order.takerAddress,
+ // order.feeRecipientAddress,
+ // order.senderAddress,
+ // order.makerAssetAmount,
+ // order.takerAssetAmount,
+ // order.makerFee,
+ // order.takerFee,
+ // order.expirationTimeSeconds,
+ // order.salt,
+ // keccak256(order.makerAssetData),
+ // keccak256(order.takerAssetData)
+ // ));
assembly {
// Backup
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
index 9e3b5a2e2..c165b647c 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
@@ -56,7 +56,7 @@ contract MExchangeCore is
event CancelUpTo(
address indexed makerAddress, // Orders cancelled must have been created by this address.
address indexed senderAddress, // Orders cancelled must have a `senderAddress` equal to this address.
- uint256 orderEpoch // Orders specified makerAddress and senderAddress with a salt <= this value are considered cancelled.
+ uint256 orderEpoch // Orders with specified makerAddress and senderAddress with a salt less than this value are considered cancelled.
);
/// @dev Updates state with results of a fill order.
diff --git a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
index 97801166a..9272b18a8 100644
--- a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
+++ b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
@@ -22,7 +22,10 @@ import "../Mintable/Mintable.sol";
import "../../utils/Ownable/Ownable.sol";
-contract DummyERC20Token is Mintable, Ownable {
+contract DummyERC20Token is
+ Mintable,
+ Ownable
+{
string public name;
string public symbol;
uint256 public decimals;
diff --git a/packages/contracts/src/2.0.0/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/packages/contracts/src/2.0.0/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
index 07986f4bb..ad71fc9a1 100644
--- a/packages/contracts/src/2.0.0/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
+++ b/packages/contracts/src/2.0.0/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol
@@ -21,7 +21,9 @@ pragma solidity 0.4.24;
import "../../protocol/Exchange/MixinAssetProxyDispatcher.sol";
-contract TestAssetProxyDispatcher is MixinAssetProxyDispatcher {
+contract TestAssetProxyDispatcher is
+ MixinAssetProxyDispatcher
+{
function publicDispatchTransferFrom(
bytes memory assetData,
address from,
diff --git a/packages/contracts/src/2.0.0/test/TestConstants/TestConstants.sol b/packages/contracts/src/2.0.0/test/TestConstants/TestConstants.sol
new file mode 100644
index 000000000..1275d007b
--- /dev/null
+++ b/packages/contracts/src/2.0.0/test/TestConstants/TestConstants.sol
@@ -0,0 +1,57 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../../utils/LibBytes/LibBytes.sol";
+
+
+// solhint-disable max-line-length
+contract TestConstants {
+
+ using LibBytes for bytes;
+
+ bytes4 constant internal ERC20_PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
+
+ address constant internal KOVAN_ZRX_ADDRESS = 0x6Ff6C0Ff1d68b964901F986d4C9FA3ac68346570;
+ bytes constant internal KOVAN_ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6f\xf6\xc0\xff\x1d\x68\xb9\x64\x90\x1f\x98\x6d\x4c\x9f\xa3\xac\x68\x34\x65\x70";
+
+ address constant internal MAINNET_ZRX_ADDRESS = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;
+ bytes constant public MAINNET_ZRX_ASSET_DATA = "\xf4\x72\x61\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe4\x1d\x24\x89\x57\x1d\x32\x21\x89\x24\x6d\xaf\xa5\xeb\xde\x1f\x46\x99\xf4\x98";
+
+ function assertValidZrxAssetData()
+ public
+ pure
+ returns (bool)
+ {
+ bytes memory kovanZrxAssetData = abi.encodeWithSelector(ERC20_PROXY_ID, KOVAN_ZRX_ADDRESS);
+ require(
+ kovanZrxAssetData.equals(KOVAN_ZRX_ASSET_DATA),
+ "INVALID_KOVAN_ZRX_ASSET_DATA"
+ );
+
+ bytes memory mainetZrxAssetData = abi.encodeWithSelector(ERC20_PROXY_ID, MAINNET_ZRX_ADDRESS);
+ require(
+ mainetZrxAssetData.equals(MAINNET_ZRX_ASSET_DATA),
+ "INVALID_MAINNET_ZRX_ASSET_DATA"
+ );
+
+ return true;
+ }
+}
+// solhint-enable max-line-length \ No newline at end of file
diff --git a/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol b/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol
index 190989181..63a2a085f 100644
--- a/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol
+++ b/packages/contracts/src/2.0.0/utils/SafeMath/SafeMath.sol
@@ -34,7 +34,7 @@ contract SafeMath {
{
require(
b <= a,
- "UINT256_OVERFLOW"
+ "UINT256_UNDERFLOW"
);
return a - b;
}