Allow v2 orders to be filled if their makerAssetFeeData field uses a v2 order id
This commit is contained in:
		@@ -32,12 +32,14 @@ contract Forwarder is
 | 
			
		||||
{
 | 
			
		||||
    constructor (
 | 
			
		||||
        address _exchange,
 | 
			
		||||
        address _exchangeV2,
 | 
			
		||||
        address _weth
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        Ownable()
 | 
			
		||||
        LibConstants(
 | 
			
		||||
            _exchange,
 | 
			
		||||
            _exchangeV2,
 | 
			
		||||
            _weth
 | 
			
		||||
        )
 | 
			
		||||
        MixinForwarderCore()
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
import "./libs/LibConstants.sol";
 | 
			
		||||
import "./libs/LibForwarderRichErrors.sol";
 | 
			
		||||
import "./interfaces/IExchangeV2.sol";
 | 
			
		||||
import "./MixinAssets.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -54,23 +55,19 @@ contract MixinExchangeWrapper is
 | 
			
		||||
        internal
 | 
			
		||||
        returns (LibFillResults.FillResults memory fillResults)
 | 
			
		||||
    {
 | 
			
		||||
        // ABI encode calldata for `fillOrder`
 | 
			
		||||
        bytes memory fillOrderCalldata = abi.encodeWithSelector(
 | 
			
		||||
            IExchange(address(0)).fillOrder.selector,
 | 
			
		||||
        if (order.makerFeeAssetData.readBytes4(0) == EXCHANGE_V2_ORDER_ID) {
 | 
			
		||||
            return _fillV2OrderNoThrow(
 | 
			
		||||
                order,
 | 
			
		||||
                takerAssetFillAmount,
 | 
			
		||||
                signature
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return _fillV3OrderNoThrow(
 | 
			
		||||
            order,
 | 
			
		||||
            takerAssetFillAmount,
 | 
			
		||||
            signature
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        address exchange = address(EXCHANGE);
 | 
			
		||||
        (bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
 | 
			
		||||
        if (didSucceed) {
 | 
			
		||||
            assert(returnData.length == 160);
 | 
			
		||||
            fillResults = abi.decode(returnData, (LibFillResults.FillResults));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fillResults values will be 0 by default if call was unsuccessful
 | 
			
		||||
        return fillResults;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Executes a single call of fillOrder according to the wethSellAmount and
 | 
			
		||||
@@ -370,6 +367,98 @@ contract MixinExchangeWrapper is
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Fills the input ExchangeV2 order. The `makerFeeAssetData` must be
 | 
			
		||||
    //       equal to EXCHANGE_V2_ORDER_ID (0x770501f8).
 | 
			
		||||
    ///      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 _fillV2OrderNoThrow(
 | 
			
		||||
        LibOrder.Order memory order,
 | 
			
		||||
        uint256 takerAssetFillAmount,
 | 
			
		||||
        bytes memory signature
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (LibFillResults.FillResults memory fillResults)
 | 
			
		||||
    {
 | 
			
		||||
        // Strip v3 specific fields from order
 | 
			
		||||
        IExchangeV2.Order memory v2Order = IExchangeV2.Order({
 | 
			
		||||
            makerAddress: order.makerAddress,
 | 
			
		||||
            takerAddress: order.takerAddress,
 | 
			
		||||
            feeRecipientAddress: order.feeRecipientAddress,
 | 
			
		||||
            senderAddress: order.senderAddress,
 | 
			
		||||
            makerAssetAmount: order.makerAssetAmount,
 | 
			
		||||
            takerAssetAmount: order.takerAssetAmount,
 | 
			
		||||
            makerFee: order.makerFee,
 | 
			
		||||
            takerFee: order.makerFee,
 | 
			
		||||
            expirationTimeSeconds: order.expirationTimeSeconds,
 | 
			
		||||
            salt: order.salt,
 | 
			
		||||
            makerAssetData: order.makerAssetData,
 | 
			
		||||
            takerAssetData: order.takerAssetData
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // ABI encode calldata for `fillOrder`
 | 
			
		||||
        bytes memory fillOrderCalldata = abi.encodeWithSelector(
 | 
			
		||||
            IExchangeV2(address(0)).fillOrder.selector,
 | 
			
		||||
            v2Order,
 | 
			
		||||
            takerAssetFillAmount,
 | 
			
		||||
            signature
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        address exchange = address(EXCHANGE_V2);
 | 
			
		||||
        (bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
 | 
			
		||||
        if (didSucceed) {
 | 
			
		||||
            assert(returnData.length == 128);
 | 
			
		||||
            IExchangeV2.FillResults memory v2FillResults = abi.decode(returnData, (IExchangeV2.FillResults));
 | 
			
		||||
 | 
			
		||||
            // Add `protocolFeePaid` field to v2 fill results
 | 
			
		||||
            fillResults = LibFillResults.FillResults({
 | 
			
		||||
                makerAssetFilledAmount: v2FillResults.makerAssetFilledAmount,
 | 
			
		||||
                takerAssetFilledAmount: v2FillResults.takerAssetFilledAmount,
 | 
			
		||||
                makerFeePaid: v2FillResults.makerFeePaid,
 | 
			
		||||
                takerFeePaid: v2FillResults.takerFeePaid,
 | 
			
		||||
                protocolFeePaid: 0
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fillResults values will be 0 by default if call was unsuccessful
 | 
			
		||||
        return fillResults;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Fills the input ExchangeV3 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 _fillV3OrderNoThrow(
 | 
			
		||||
        LibOrder.Order memory order,
 | 
			
		||||
        uint256 takerAssetFillAmount,
 | 
			
		||||
        bytes memory signature
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (LibFillResults.FillResults memory fillResults)
 | 
			
		||||
    {
 | 
			
		||||
        // ABI encode calldata for `fillOrder`
 | 
			
		||||
        bytes memory fillOrderCalldata = abi.encodeWithSelector(
 | 
			
		||||
            IExchange(address(0)).fillOrder.selector,
 | 
			
		||||
            order,
 | 
			
		||||
            takerAssetFillAmount,
 | 
			
		||||
            signature
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        address exchange = address(EXCHANGE);
 | 
			
		||||
        (bool didSucceed, bytes memory returnData) = exchange.call(fillOrderCalldata);
 | 
			
		||||
        if (didSucceed) {
 | 
			
		||||
            assert(returnData.length == 160);
 | 
			
		||||
            fillResults = abi.decode(returnData, (LibFillResults.FillResults));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // fillResults values will be 0 by default if call was unsuccessful
 | 
			
		||||
        return fillResults;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Checks whether one asset is effectively equal to another asset.
 | 
			
		||||
    ///      This is the case if they have the same ERC20Proxy/ERC20BridgeProxy asset data, or if
 | 
			
		||||
    ///      one is the ERC20Bridge equivalent of the other.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,60 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 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.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract IExchangeV2 {
 | 
			
		||||
 | 
			
		||||
    // solhint-disable max-line-length
 | 
			
		||||
    struct Order {
 | 
			
		||||
        address makerAddress;           // Address that created the order.      
 | 
			
		||||
        address takerAddress;           // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.          
 | 
			
		||||
        address feeRecipientAddress;    // Address that will recieve fees when order is filled.      
 | 
			
		||||
        address senderAddress;          // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
 | 
			
		||||
        uint256 makerAssetAmount;       // Amount of makerAsset being offered by maker. Must be greater than 0.        
 | 
			
		||||
        uint256 takerAssetAmount;       // Amount of takerAsset being bid on by maker. Must be greater than 0.        
 | 
			
		||||
        uint256 makerFee;               // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
 | 
			
		||||
        uint256 takerFee;               // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
 | 
			
		||||
        uint256 expirationTimeSeconds;  // Timestamp in seconds at which order expires.          
 | 
			
		||||
        uint256 salt;                   // Arbitrary number to facilitate uniqueness of the order's hash.     
 | 
			
		||||
        bytes makerAssetData;           // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.
 | 
			
		||||
        bytes takerAssetData;           // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.
 | 
			
		||||
    }
 | 
			
		||||
    // solhint-enable max-line-length
 | 
			
		||||
 | 
			
		||||
    struct FillResults {
 | 
			
		||||
        uint256 makerAssetFilledAmount;  // Total amount of makerAsset(s) filled.
 | 
			
		||||
        uint256 takerAssetFilledAmount;  // Total amount of takerAsset(s) filled.
 | 
			
		||||
        uint256 makerFeePaid;            // Total amount of ZRX paid by maker(s) to feeRecipient(s).
 | 
			
		||||
        uint256 takerFeePaid;            // Total amount of ZRX paid by taker to feeRecipients(s).
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Fills the input order.
 | 
			
		||||
    /// @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 fillOrder(
 | 
			
		||||
        Order memory order,
 | 
			
		||||
        uint256 takerAssetFillAmount,
 | 
			
		||||
        bytes memory signature
 | 
			
		||||
    )
 | 
			
		||||
        public;
 | 
			
		||||
}
 | 
			
		||||
@@ -18,29 +18,49 @@
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
 | 
			
		||||
import "../interfaces/IExchangeV2.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract LibConstants {
 | 
			
		||||
 | 
			
		||||
    using LibBytes for bytes;
 | 
			
		||||
    uint256 constant internal MAX_UINT = uint256(-1);
 | 
			
		||||
 | 
			
		||||
    uint256 constant internal MAX_UINT = 2**256 - 1;
 | 
			
		||||
    // The v2 order id is the first 4 bytes of the ExchangeV2 order shema hash.
 | 
			
		||||
    // bytes4(keccak256(abi.encodePacked(
 | 
			
		||||
    //     "Order(",
 | 
			
		||||
    //     "address makerAddress,",
 | 
			
		||||
    //     "address takerAddress,",
 | 
			
		||||
    //     "address feeRecipientAddress,",
 | 
			
		||||
    //     "address senderAddress,",
 | 
			
		||||
    //     "uint256 makerAssetAmount,",
 | 
			
		||||
    //     "uint256 takerAssetAmount,",
 | 
			
		||||
    //     "uint256 makerFee,",
 | 
			
		||||
    //     "uint256 takerFee,",
 | 
			
		||||
    //     "uint256 expirationTimeSeconds,",
 | 
			
		||||
    //     "uint256 salt,",
 | 
			
		||||
    //     "bytes makerAssetData,",
 | 
			
		||||
    //     "bytes takerAssetData",
 | 
			
		||||
    //     ")"
 | 
			
		||||
    // )));
 | 
			
		||||
    bytes4 constant public EXCHANGE_V2_ORDER_ID = 0x770501f8;
 | 
			
		||||
 | 
			
		||||
     // solhint-disable var-name-mixedcase
 | 
			
		||||
    IExchange internal EXCHANGE;
 | 
			
		||||
    IExchangeV2 internal EXCHANGE_V2;
 | 
			
		||||
    IEtherToken internal ETHER_TOKEN;
 | 
			
		||||
    // solhint-enable var-name-mixedcase
 | 
			
		||||
 | 
			
		||||
    constructor (
 | 
			
		||||
        address _exchange,
 | 
			
		||||
        address _exchangeV2,
 | 
			
		||||
        address _weth
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        EXCHANGE = IExchange(_exchange);
 | 
			
		||||
        EXCHANGE_V2 = IExchangeV2(_exchangeV2);
 | 
			
		||||
        ETHER_TOKEN = IEtherToken(_weth);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ contract TestForwarder is
 | 
			
		||||
    constructor ()
 | 
			
		||||
        public
 | 
			
		||||
        LibConstants(
 | 
			
		||||
            address(0),
 | 
			
		||||
            address(0),
 | 
			
		||||
            address(0)
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user