aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0/forwarder/MixinMarketSellTokens.sol
blob: 8c9cdb8d592effcc393bfb38bf2dbddde1db5b4b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*

  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 "../utils/LibBytes/LibBytes.sol";
import "./MixinWethFees.sol";
import "./MixinExpectedResults.sol";
import "./MixinERC20.sol";
import "./MixinConstants.sol";
import "./MixinMarketBuyZrx.sol";

contract MixinMarketSellTokens is
    MixinConstants,
    MixinWethFees,
    MixinMarketBuyZrx,
    MixinExpectedResults,
    MixinERC20
{
    /// @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(
        LibOrder.Order[] memory orders,
        bytes[] memory signatures,
        LibOrder.Order[] memory feeOrders,
        bytes[] memory feeSignatures,
        uint16  feeProportion,
        address feeRecipient
    )
        payable
        public
        returns (FillResults memory totalFillResults)
    {
        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(
                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"
        );
        // Ensure no WETH is left in this contract
        require(
            wethSellAmount == totalFillResults.takerAssetFilledAmount,
            "UNACCEPTABLE_THRESHOLD"
        );
        // Transfer all tokens to msg.sender
        transferToken(
            makerTokenAddress,
            msg.sender,
            totalFillResults.makerAssetFilledAmount
        );
        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;
    }

    /// @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 (FillResults memory totalFillResults)
    {
        // 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);
        require(
            isAcceptableThreshold(totalFillResults.makerAssetFilledAmount, zrxAmountBought),
            "UNACCEPTABLE_THRESHOLD"
        );
        totalFillResults.makerAssetFilledAmount = zrxAmountBought;
        return totalFillResults;
    }

}