aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Bandeali <abandeali1@gmail.com>2018-07-10 12:09:45 +0800
committerAmir Bandeali <abandeali1@gmail.com>2018-07-23 23:00:23 +0800
commit814518dd8094d03908d77e39faa21b8758f1552b (patch)
tree7da9bd14251b2f1e3c23a8bbbd844c11c8a2b1d0
parent554b18a466d5cc65271daa61aba96138ce7408f0 (diff)
downloaddexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar.gz
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar.bz2
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar.lz
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar.xz
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.tar.zst
dexon-sol-tools-814518dd8094d03908d77e39faa21b8758f1552b.zip
Refactor forwarding contract architecture, remove batch functions
-rw-r--r--packages/contracts/src/2.0.0/forwarder/Forwarder.sol13
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinAssets.sol (renamed from packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol)77
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinConstants.sol4
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol18
-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.sol519
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinMarketBuyZrx.sol83
-rw-r--r--packages/contracts/src/2.0.0/forwarder/MixinWeth.sol109
-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)41
-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/mixins/MAssets.sol50
-rw-r--r--packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol4
-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.sol76
-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.sol43
20 files changed, 572 insertions, 1038 deletions
diff --git a/packages/contracts/src/2.0.0/forwarder/Forwarder.sol b/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
index 546e7f22c..4405d5e46 100644
--- a/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
+++ b/packages/contracts/src/2.0.0/forwarder/Forwarder.sol
@@ -19,20 +19,17 @@
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 "./MixinAssets.sol";
+// solhint-disable no-empty-blocks
contract Forwarder is
MixinConstants,
- MixinExpectedResults,
- MixinFees,
- MixinMarketBuyZrx,
- MixinTransfer,
+ MixinWeth,
+ MixinAssets,
MixinForwarderCore
{
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol b/packages/contracts/src/2.0.0/forwarder/MixinAssets.sol
index bebfc976b..325af5518 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinTransfer.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinAssets.sol
@@ -19,12 +19,17 @@
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 "./mixins/MAssets.sol";
+import "./mixins/MConstants.sol";
-contract MixinTransfer is
- MTransfer
+contract MixinAssets is
+ Ownable,
+ MConstants,
+ MAssets
{
using LibBytes for bytes;
@@ -33,6 +38,24 @@ contract MixinTransfer is
bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)"));
bytes4 constant internal ERC721_RECEIVED_OPERATOR = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
+ /// @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
+ )
+ external
+ onlyOwner
+ {
+ require(
+ IERC20Token(token).transfer(msg.sender, amount),
+ "WITHDRAWAL_FAILED"
+ );
+ }
+
function onERC721Received(
address,
uint256,
@@ -58,19 +81,43 @@ contract MixinTransfer is
return ERC721_RECEIVED_OPERATOR;
}
+ /// @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
+ {
+ bytes4 proxyId = assetData.readBytes4(0);
+
+ if (proxyId == ERC20_DATA_ID) {
+ transferERC20Token(assetData, amount);
+ } else if (proxyId == ERC721_DATA_ID) {
+ transferERC721Token(assetData);
+ } 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,19 +147,23 @@ contract MixinTransfer is
);
}
- function transferERC721Token(
- bytes memory assetData,
- address to
- )
+ /// @dev Decodes ERC721 assetData and transfers given amount to sender.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ function transferERC721Token(bytes memory assetData)
internal
{
// Decode asset data.
address token = assetData.readAddress(16);
uint256 tokenId = assetData.readUint256(36);
- IERC721Token(token).transferFrom(
+ bytes memory receiverData = assetData.readBytesWithLength(100);
+
+ // Perform transfer.
+ // TODO: Do we want to use `transferFrom` here?
+ IERC721Token(token).safeTransferFrom(
address(this),
- to,
- tokenId
+ msg.sender,
+ tokenId,
+ receiverData
);
}
}
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol b/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol
index 2b064d579..4f95c796b 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinConstants.sol
@@ -25,6 +25,10 @@ contract MixinConstants is
MConstants
{
+ 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;
+
constructor (
address _exchange,
address _etherToken,
diff --git a/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol b/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol
index 1b3e3f488..91758eadc 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinErrorMessages.sol
@@ -22,14 +22,12 @@ 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";
+ string constant FEE_PERCENTAGE_TOO_LARGE = "FEE_PROPORTION_TOO_LARGE"; // Provided fee percentage greater than 5%.
+ string constant MAX_FEE_EXCEEDED = "MAX_FEE_EXCEEDED"; // 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.
}
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 cb0ed5422..1a0687ab5 100644
--- a/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol
+++ b/packages/contracts/src/2.0.0/forwarder/MixinForwarderCore.sol
@@ -19,30 +19,25 @@
pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;
-import "../utils/LibBytes/LibBytes.sol";
-import "./mixins/MFees.sol";
-import "./mixins/MMarketBuyZrx.sol";
-import "./mixins/MExpectedResults.sol";
-import "./mixins/MTransfer.sol";
+import "./mixins/MWeth.sol";
+import "./mixins/MAssets.sol";
import "./mixins/MConstants.sol";
import "./mixins/MForwarderCore.sol";
import "../protocol/Exchange/libs/LibOrder.sol";
import "../protocol/Exchange/libs/LibFillResults.sol";
+import "../protocol/Exchange/libs/LibMath.sol";
contract MixinForwarderCore is
LibFillResults,
+ LibMath,
MConstants,
- MExpectedResults,
- MFees,
- MMarketBuyZrx,
- MTransfer,
+ MWeth,
+ MAssets,
MForwarderCore
{
- bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)"));
- bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)"));
- uint256 constant internal MAX_UINT = 2**256 - 1;
+ /// @dev Constructor approves ERC20 proxy to transfer ZRX and WETH on this contract's behalf.
constructor ()
public
{
@@ -53,377 +48,257 @@ contract MixinForwarderCore is
}
}
- /// @dev Market sells ETH for ERC20 tokens, performing fee abstraction if required. This does not support ERC721 tokens. This function is payable
- /// and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// This function allows for a deduction of a proportion of incoming ETH sent to the feeRecipient.
- /// The caller is sent all tokens from the operation.
- /// If the purchased token amount does not meet an acceptable threshold then this function reverts.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the incoming ETH and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20(
+ /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketSellOrdersWithEth(
LibOrder.Order[] memory orders,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (FillResults memory totalFillResults)
+ returns (
+ FillResults memory orderFillResults,
+ FillResults memory feeOrderFillResults
+ )
{
- uint256 takerEthAmount = msg.value;
- require(
- takerEthAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
- );
- // Deduct the fee from the total amount of ETH sent in
- uint256 ethFeeAmount = payEthFee(
- takerEthAmount,
- feeProportion,
- feeRecipient
+ // Convert ETH to WETH.
+ // 5% of WETH is reserved for filling feeOrders and paying feeRecipient.
+ uint256 wethAvailable = convertEthToWeth();
+
+ // Attempt to sell 95% of WETH.
+ // ZRX fees are payed with this contract's balance.
+ marketSellEth(
+ orders,
+ wethAvailable,
+ signatures
);
- uint256 wethSellAmount = safeSub(takerEthAmount, ethFeeAmount);
- // Deposit the remaining to be used for trading
- ETHER_TOKEN.deposit.value(wethSellAmount)();
- // Populate the known assetData, as it is always WETH the caller can provide null bytes to save gas
- // marketSellOrders fills the remaining
- address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16);
- orders[0].takerAssetData = WETH_ASSET_DATA;
- if (makerTokenAddress == address(ZRX_TOKEN)) {
- // If this is ZRX then we market sell from the orders, rather than a 2 step of buying ZRX fees from feeOrders
- // then buying ZRX from orders
- totalFillResults = marketSellEthForZRXInternal(
- orders,
- signatures,
- wethSellAmount
- );
- } else {
- totalFillResults = marketSellEthForERC20Internal(
- orders,
- signatures,
- feeOrders,
- feeSignatures,
- wethSellAmount
- );
- }
- // Prevent accidental WETH owned by this contract and it being spent
- require(
- takerEthAmount >= totalFillResults.takerAssetFilledAmount,
- "INVALID_MSG_VALUE"
+ // Buy back all ZRX spent on fees.
+ feeOrderFillResults = marketBuyZrx(
+ feeOrders,
+ orderFillResults.takerFeePaid,
+ feeSignatures
);
- // Ensure no WETH is left in this contract
+
+ // Ensure that no extra WETH owned by this contract has been sold.
+ uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount);
require(
- wethSellAmount == totalFillResults.takerAssetFilledAmount,
- "UNACCEPTABLE_THRESHOLD"
+ totalWethSold <= msg.value,
+ "OVERSOLD_WETH"
);
- // Transfer all tokens to msg.sender
- transferERC20Token(
- makerTokenAddress,
- msg.sender,
- totalFillResults.makerAssetFilledAmount
+
+ // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
+ // Refund remaining ETH to msg.sender.
+ transferEthFeeAndRefund(
+ orderFillResults.takerAssetFilledAmount,
+ feeOrderFillResults.takerAssetFilledAmount,
+ feePercentage,
+ feeRecipient
);
- return totalFillResults;
+
+ // Transfer purchased assets to msg.sender.
+ transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount);
}
- /// @dev Buys the exact amount of assets (ERC20 and ERC721), performing fee abstraction if required.
- /// All order assets must be of the same type. Deducts a proportional fee to fee recipient.
- /// This function is payable and will convert all incoming ETH into WETH and perform the trade on behalf of the caller.
- /// The caller is sent all assets from the fill of orders. This function will revert unless the requested amount of assets are purchased.
- /// Any excess ETH sent will be returned to the caller
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param makerTokenFillAmount The amount of maker asset to buy.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param feeProportion A proportion deducted off the ETH spent and sent to feeRecipient. The maximum value for this
- /// is 1000, aka 10%. Supports up to 2 decimal places. I.e 0.59% is 59.
- /// @param feeRecipient An address of the fee recipient whom receives feeProportion of ETH.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketBuyTokensWithEth(
+ /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction.
+ /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
+ /// Any ETH not spent will be refunded to sender.
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to purchase.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees.
+ /// @param feeSignatures Proofs that feeOrders have been created by makers.
+ /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ /// @param feeRecipient Address that will receive ETH when orders are filled.
+ /// @return Amounts filled and fees paid by maker and taker for both sets of orders.
+ function marketBuyOrdersWithEth(
LibOrder.Order[] memory orders,
+ uint256 makerAssetFillAmount,
bytes[] memory signatures,
LibOrder.Order[] memory feeOrders,
bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount,
- uint16 feeProportion,
+ uint256 feePercentage,
address feeRecipient
)
public
payable
- returns (FillResults memory totalFillResults)
+ returns (
+ FillResults memory orderFillResults,
+ FillResults memory feeOrderFillResults
+ )
{
- uint256 takerEthAmount = msg.value;
- require(
- takerEthAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
- );
- require(
- makerTokenFillAmount > 0,
- "VALUE_GREATER_THAN_ZERO"
+ // Convert ETH to WETH.
+ convertEthToWeth();
+
+ // Attemp to purchase desired amount of makerAsset.
+ // ZRX fees are payed with this contract's balance.
+ orderFillResults = marketBuyAsset(
+ orders,
+ makerAssetFillAmount,
+ signatures
);
- bytes4 assetDataId = LibBytes.readBytes4(orders[0].makerAssetData, 0);
- require(
- assetDataId == ERC20_DATA_ID || assetDataId == ERC721_DATA_ID,
- "UNSUPPORTED_TOKEN_PROXY"
+
+ // Buy back all ZRX spent on fees.
+ feeOrderFillResults = marketBuyZrx(
+ feeOrders,
+ orderFillResults.takerFeePaid,
+ feeSignatures
);
- ETHER_TOKEN.deposit.value(takerEthAmount)();
- if (assetDataId == ERC20_DATA_ID) {
- totalFillResults = marketBuyERC20TokensInternal(
- orders,
- signatures,
- feeOrders,
- feeSignatures,
- makerTokenFillAmount
- );
- } else if (assetDataId == ERC721_DATA_ID) {
- totalFillResults = batchBuyERC721TokensInternal(
- orders,
- signatures,
- feeOrders,
- feeSignatures
- );
- }
- // Prevent accidental WETH owned by this contract and it being spent
+ // Ensure that no extra WETH owned by this contract has been sold.
+ uint256 totalWethSold = safeAdd(orderFillResults.takerAssetFilledAmount, feeOrderFillResults.takerAssetFilledAmount);
require(
- takerEthAmount >= totalFillResults.takerAssetFilledAmount,
- "INVALID_MSG_VALUE"
+ totalWethSold <= msg.value,
+ "OVERSOLD_WETH"
);
- withdrawPayAndDeductEthFee(
- safeSub(takerEthAmount, totalFillResults.takerAssetFilledAmount),
- totalFillResults.takerAssetFilledAmount,
- feeProportion,
+
+ // Transfer feePercentage of total ETH spent on primary orders to feeRecipient.
+ // Refund remaining ETH to msg.sender.
+ transferEthFeeAndRefund(
+ orderFillResults.takerAssetFilledAmount,
+ feeOrderFillResults.takerAssetFilledAmount,
+ feePercentage,
feeRecipient
);
- return totalFillResults;
+
+ // Transfer purchased assets to msg.sender.
+ transferPurchasedAssetToSender(orders[0].makerAssetData, orderFillResults.makerAssetFilledAmount);
}
- /// @dev Market sells WETH for ERC20 tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param feeOrders An array of Order struct containing order specifications for fees.
- /// @param feeSignatures An array of Proof that order has been created by maker for the fee orders.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForERC20Internal(
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param wethSellAmount Desired amount of WETH to sell.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function marketSellEth(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 wethSellAmount
+ uint256 wethSellAmount,
+ bytes[] memory signatures
)
internal
- returns (FillResults memory totalFillResults)
+ returns (FillResults memory fillResults)
{
- uint256 remainingWethSellAmount = wethSellAmount;
- FillResults memory calculatedMarketSellResults = calculateMarketSellResults(orders, wethSellAmount);
- if (calculatedMarketSellResults.takerFeePaid > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future market buy
- FillResults memory feeTokensResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- calculatedMarketSellResults.takerFeePaid
- );
- // Ensure the token abstraction was fair if fees were proportionally too high, we fail
- require(
- isAcceptableThreshold(
- wethSellAmount,
- safeSub(wethSellAmount, feeTokensResults.takerAssetFilledAmount)
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- remainingWethSellAmount = safeSub(remainingWethSellAmount, feeTokensResults.takerAssetFilledAmount);
- totalFillResults.takerFeePaid = feeTokensResults.takerFeePaid;
- totalFillResults.takerAssetFilledAmount = feeTokensResults.takerAssetFilledAmount;
+ // `marketSellOrders` uses the first order's takerAssetData for all passed in orders.
+ orders[0].takerAssetData = WETH_ASSET_DATA;
+
+ // All orders are required to have the same makerAssetData. We save on gas by reusing the makerAssetData of the first order.
+ for (uint256 i = 0; i < orders.length; i++) {
+ orders[i].makerAssetData = orders[0].makerAssetData;
}
- // Make our market sell to buy the requested tokens with the remaining balance
- FillResults memory requestedTokensResults = EXCHANGE.marketSellOrders(
+
+ // Sell WETH until entire amount has been sold or all orders have been filled.
+ fillResults = EXCHANGE.marketSellOrdersNoThrow(
orders,
- remainingWethSellAmount,
+ wethSellAmount,
signatures
);
- // Update our return FillResult with the market sell
- addFillResults(totalFillResults, requestedTokensResults);
- return totalFillResults;
+
+ return fillResults;
}
- /// @dev Market sells WETH for ZRX tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForZRXInternal(
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function marketBuyAsset(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 wethSellAmount
+ uint256 makerAssetFillAmount,
+ bytes[] memory signatures
)
internal
- returns (FillResults memory totalFillResults)
+ returns (FillResults memory fillResults)
{
- // Make our market sell to buy the requested tokens with the remaining balance
- totalFillResults = EXCHANGE.marketSellOrders(
+ bytes memory wethAssetData = WETH_ASSET_DATA;
+
+ // All orders are required to have WETH as takerAssetData. We save on gas by populating the orders here, rather than passing in any extra calldata.
+ for (uint256 i = 0; i < orders.length; i++) {
+ orders[i].takerAssetData = wethAssetData;
+ }
+
+ // Purchase makerAsset until entire amount has been bought or all orders have been filled.
+ fillResults = EXCHANGE.marketBuyOrdersNoThrow(
orders,
- wethSellAmount,
+ makerAssetFillAmount,
signatures
);
- // Exchange does not special case ZRX in the makerAssetFilledAmount, if fees were deducted then using this amount
- // for future transfers is invalid.
- uint256 zrxAmountBought = safeSub(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid);
- require(
- isAcceptableThreshold(totalFillResults.makerAssetFilledAmount, zrxAmountBought),
- "UNACCEPTABLE_THRESHOLD"
- );
- totalFillResults.makerAssetFilledAmount = zrxAmountBought;
- return totalFillResults;
+
+ return fillResults;
}
- /// @dev Buys an exact amount of an ERC20 token using WETH.
- /// @param orders Orders to fill. The maker asset is the ERC20 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @param makerTokenFillAmount Amount of the ERC20 token to buy.
- /// @return totalFillResults Aggregated fill results of buying the ERC20 and ZRX tokens.
- function marketBuyERC20TokensInternal(
+ /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee
+ /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).
+ /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX
+ /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
+ /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.
+ /// @param zrxBuyAmount Desired amount of ZRX to buy.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return totalFillResults Amounts filled and fees paid by maker and taker.
+ function marketBuyZrx(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 makerTokenFillAmount
+ uint256 zrxBuyAmount,
+ bytes[] memory signatures
)
internal
- returns (LibFillResults.FillResults memory totalFillResults)
+ returns (FillResults memory totalFillResults)
{
- // We read the maker token address to check if it is ZRX and later use it for transfer
- address makerTokenAddress = LibBytes.readAddress(orders[0].makerAssetData, 16);
- // We assume that asset being bought by taker is the same for each order.
- // Rather than passing this in as calldata, we copy the makerAssetData from the first order onto all later orders.
- orders[0].takerAssetData = WETH_ASSET_DATA;
- // We can short cut here for effeciency and use buyFeeTokensInternal if maker asset token is ZRX
- // this buys us exactly that amount taking into account the fees. This saves gas and calculates the rate correctly
- FillResults memory marketBuyResults;
- if (makerTokenAddress == address(ZRX_TOKEN)) {
- marketBuyResults = marketBuyZrxInternal(
- orders,
- signatures,
- makerTokenFillAmount
- );
- // When buying ZRX we round up which can result in a small margin excess
- require(
- marketBuyResults.makerAssetFilledAmount >= makerTokenFillAmount,
- "UNACCEPTABLE_THRESHOLD"
- );
- addFillResults(totalFillResults, marketBuyResults);
- require(
- isAcceptableThreshold(
- safeAdd(totalFillResults.makerAssetFilledAmount, totalFillResults.takerFeePaid), // Total ZRX
- totalFillResults.makerAssetFilledAmount // amount going to msg.sender
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- } else {
- FillResults memory calculatedMarketBuyResults = calculateMarketBuyResults(orders, makerTokenFillAmount);
- if (calculatedMarketBuyResults.takerFeePaid > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future market buy
- FillResults memory zrxMarketBuyResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- calculatedMarketBuyResults.takerFeePaid
+ // Do nothing if zrxBuyAmount == 0
+ if (zrxBuyAmount > 0) {
+
+ bytes memory zrxAssetData = ZRX_ASSET_DATA;
+ bytes memory wethAssetData = WETH_ASSET_DATA;
+
+ for (uint256 i = 0; i < orders.length; i++) {
+
+ // All of these are ZRX/WETH, so we can drop the respective assetData from calldata.
+ orders[i].makerAssetData = zrxAssetData;
+ orders[i].takerAssetData = wethAssetData;
+
+ // Calculate the remaining amount of ZRX to buy.
+ uint256 remainingZrxBuyAmount = safeAdd(
+ safeSub(zrxBuyAmount, totalFillResults.makerAssetFilledAmount),
+ totalFillResults.takerFeePaid
+ );
+
+ // Convert the remaining amount of ZRX to buy into remaining amount
+ // of WETH to sell, assuming entire amount can be sold in the current order.
+ uint256 remainingWethSellAmount = getPartialAmount(
+ orders[i].takerAssetAmount,
+ safeSub(orders[i].makerAssetAmount, orders[i].takerFee), // our exchange rate after fees
+ remainingZrxBuyAmount
+ );
+
+ // Attempt to sell the remaining amount of WETH.
+ FillResults memory singleFillResult = EXCHANGE.fillOrderNoThrow(
+ orders[i],
+ remainingWethSellAmount,
+ signatures[i]
);
- totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount;
- totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid;
+
+ // Update amounts filled and fees paid by maker and taker.
+ addFillResults(totalFillResults, singleFillResult);
+
+ // Stop execution if the entire amount of ZRX has been bought.
+ if (totalFillResults.makerAssetFilledAmount == zrxBuyAmount) {
+ break;
+ }
}
- // Make our market buy of the requested tokens with the remaining balance
- marketBuyResults = EXCHANGE.marketBuyOrders(
- orders,
- makerTokenFillAmount,
- signatures
- );
- require(
- marketBuyResults.makerAssetFilledAmount == makerTokenFillAmount,
- "UNACCEPTABLE_THRESHOLD"
- );
- addFillResults(totalFillResults, marketBuyResults);
- require(
- isAcceptableThreshold(
- totalFillResults.takerAssetFilledAmount,
- marketBuyResults.takerAssetFilledAmount
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- }
- // Transfer all purchased tokens to msg.sender
- transferERC20Token(
- makerTokenAddress,
- msg.sender,
- marketBuyResults.makerAssetFilledAmount
- );
- return totalFillResults;
- }
- /// @dev Buys an all of the ERC721 tokens in the orders.
- /// @param orders Orders to fill. The maker asset is the ERC721 token to buy. The taker asset is WETH.
- /// @param signatures Proof that the orders were created by their respective makers.
- /// @param feeOrders to fill. The maker asset is ZRX and the taker asset is WETH.
- /// @param feeSignatures Proof that the feeOrders were created by their respective makers.
- /// @return totalFillResults Aggregated fill results of buying the ERC721 tokens and ZRX tokens.
- function batchBuyERC721TokensInternal(
- LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures
- )
- internal
- returns (LibFillResults.FillResults memory totalFillResults)
- {
- uint256 totalZrxFeeAmount;
- uint256 ordersLength = orders.length;
- uint256[] memory takerAssetFillAmounts = new uint256[](ordersLength);
- for (uint256 i = 0; i < ordersLength; i++) {
- // Total up the fees
- totalZrxFeeAmount = safeAdd(totalZrxFeeAmount, orders[i].takerFee);
- // We assume that asset being bought by taker is the same for each order.
- // Rather than passing this in as calldata, we set the takerAssetData as WETH asset data
- orders[i].takerAssetData = WETH_ASSET_DATA;
- // Populate takerAssetFillAmounts for later batchFill
- takerAssetFillAmounts[i] = orders[i].takerAssetAmount;
- }
- if (totalZrxFeeAmount > 0) {
- // Fees are required for these orders. Buy enough ZRX to cover the future fill
- FillResults memory zrxMarketBuyResults = marketBuyZrxInternal(
- feeOrders,
- feeSignatures,
- totalZrxFeeAmount
- );
- totalFillResults.takerFeePaid = zrxMarketBuyResults.takerFeePaid;
- totalFillResults.takerAssetFilledAmount = zrxMarketBuyResults.takerAssetFilledAmount;
- }
- FillResults memory batchFillResults = EXCHANGE.batchFillOrKillOrders(
- orders,
- takerAssetFillAmounts,
- signatures
- );
- addFillResults(totalFillResults, batchFillResults);
- require(
- isAcceptableThreshold(
- totalFillResults.takerAssetFilledAmount,
- batchFillResults.takerAssetFilledAmount
- ),
- "UNACCEPTABLE_THRESHOLD"
- );
- // Transfer all of the tokens filled from the batchFill
- for (i = 0; i < ordersLength; i++) {
- transferERC721Token(
- orders[i].makerAssetData,
- msg.sender
+ // Ensure that all ZRX spent while filling primary orders has been repurchased.
+ require(
+ totalFillResults.makerAssetFilledAmount == zrxBuyAmount,
+ "COMPLETE_FILL_FAILED"
);
}
return totalFillResults;
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..cbc81a11c
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/MixinWeth.sol
@@ -0,0 +1,109 @@
+/*
+
+ 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/MWeth.sol";
+
+
+contract MixinWeth is
+ LibMath,
+ MConstants,
+ MWeth
+{
+
+ uint256 constant internal PERCENTAGE_DENOMINATOR = 10000; // 9800 == 98%, 10000 == 100%
+ uint256 constant internal MAX_FEE_PERCENTAGE = 500; // 5%
+ uint256 constant internal MAX_WETH_FILL_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 Converts message call's ETH value into WETH.
+ /// @return 95% of ETH converted to WETH.
+ function convertEthToWeth()
+ internal
+ returns (uint256 wethAvailable)
+ {
+ require(
+ msg.value > 0,
+ "INVALID_MSG_VALUE"
+ );
+
+ ETHER_TOKEN.deposit.value(msg.value)();
+ wethAvailable = getPartialAmount(
+ MAX_WETH_FILL_PERCENTAGE,
+ PERCENTAGE_DENOMINATOR,
+ msg.value
+ );
+ return wethAvailable;
+ }
+
+ /// @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
+ {
+ uint256 wethRemaining = safeSub(
+ msg.value,
+ safeAdd(wethSoldExcludingFeeOrders, wethSoldForZrx)
+ );
+ ETHER_TOKEN.withdraw(wethRemaining);
+
+ require(
+ feePercentage <= MAX_FEE_PERCENTAGE,
+ "FEE_PERCENTAGE_TOO_LARGE"
+ );
+ uint256 ethFee = getPartialAmount(
+ feePercentage,
+ PERCENTAGE_DENOMINATOR,
+ wethSoldExcludingFeeOrders
+ );
+ require(
+ ethFee < wethRemaining,
+ "MAX_FEE_EXCEEDED"
+ );
+ if (ethFee > 0) {
+ feeRecipient.transfer(ethFee);
+ }
+
+ 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..27adb1221 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,35 @@
pragma solidity 0.4.24;
-contract MTransfer {
+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,
+ uint256 amount
+ )
+ external;
- function onERC721Received(address, uint256, bytes memory)
+ function onERC721Received(
+ address,
+ uint256,
+ bytes memory
+ )
public
pure
returns(bytes4);
- function onERC721Received(address, address, uint256, bytes memory)
+ function onERC721Received(
+ address,
+ address,
+ uint256,
+ bytes memory
+ )
public
pure
returns(bytes4);
-
- function transferERC20Token(
- address token,
- address to,
- uint256 amount
- )
- internal;
-
- function transferERC721Token(
- bytes memory assetData,
- address to
- )
- internal;
-}
+} \ No newline at end of file
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/mixins/MAssets.sol b/packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol
new file mode 100644
index 000000000..b69f7482d
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MAssets.sol
@@ -0,0 +1,50 @@
+/*
+
+ 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.
+ function transferERC721Token(bytes memory assetData)
+ 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
index 348bf169e..bbc4969d6 100644
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MConstants.sol
@@ -25,6 +25,10 @@ import "../../tokens/ERC20Token/IERC20Token.sol";
contract MConstants {
+ 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;
+
// solhint-disable var-name-mixedcase
IExchange internal EXCHANGE;
IEtherToken internal ETHER_TOKEN;
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..83915c738 100644
--- a/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MForwarderCore.sol
@@ -28,64 +28,42 @@ 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(
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param wethSellAmount Desired amount of WETH to sell.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function marketSellEth(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures,
- uint256 wethSellAmount
+ uint256 wethSellAmount,
+ bytes[] memory signatures
)
internal
- returns (LibFillResults.FillResults memory totalFillResults);
+ returns (LibFillResults.FillResults memory fillResults);
- /// @dev Market sells WETH for ZRX tokens.
- /// @param orders An array of Order struct containing order specifications.
- /// @param signatures An array of Proof that order has been created by maker.
- /// @param wethSellAmount The amount of WETH to sell.
- /// @return FillResults amounts filled and fees paid by maker and taker.
- function marketSellEthForZRXInternal(
+ /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset.
+ /// @param makerAssetFillAmount Desired amount of makerAsset to buy.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by maker and taker.
+ function marketBuyAsset(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- uint256 wethSellAmount
+ uint256 makerAssetFillAmount,
+ bytes[] memory signatures
)
internal
- returns (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);
+ returns (LibFillResults.FillResults memory fillResults);
- /// @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(
+ /// @dev Buys zrxBuyAmount of ZRX fee tokens, taking into account ZRX fees for each order. This will guarantee
+ /// that at least zrxBuyAmount of ZRX is purchased (sometimes slightly over due to rounding issues).
+ /// It is possible that a request to buy 200 ZRX will require purchasing 202 ZRX
+ /// as 2 ZRX is required to purchase the 200 ZRX fee tokens. This guarantees at least 200 ZRX for future purchases.
+ /// @param orders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset.
+ /// @param zrxBuyAmount Desired amount of ZRX to buy.
+ /// @param signatures Proofs that orders have been created by makers.
+ /// @return totalFillResults Amounts filled and fees paid by maker and taker.
+ function marketBuyZrx(
LibOrder.Order[] memory orders,
- bytes[] memory signatures,
- LibOrder.Order[] memory feeOrders,
- bytes[] memory feeSignatures
+ uint256 zrxBuyAmount,
+ bytes[] memory signatures
)
internal
returns (LibFillResults.FillResults memory totalFillResults);
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..7cbc115e9
--- /dev/null
+++ b/packages/contracts/src/2.0.0/forwarder/mixins/MWeth.sol
@@ -0,0 +1,43 @@
+/*
+
+ 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.
+ /// @return 95% of ETH converted to WETH.
+ function convertEthToWeth()
+ internal
+ returns (uint256 wethAvailable);
+
+ /// @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;
+}