aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0/forwarder/MixinExchangeWrapper.sol
blob: e230767b75871abb3c88b1e3e9e3416218980259 (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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
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;
    }
}