update tests
This commit is contained in:
		@@ -57,26 +57,12 @@ contract MixinExchangeWrapper is
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        address exchange = address(EXCHANGE);
 | 
			
		||||
 | 
			
		||||
        // Call `fillOrder` and handle any exceptions gracefully
 | 
			
		||||
        assembly {
 | 
			
		||||
            let success := call(
 | 
			
		||||
                gas,                                // forward all gas
 | 
			
		||||
                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
 | 
			
		||||
                160                                 // output size is 160 bytes
 | 
			
		||||
            )
 | 
			
		||||
            if success {
 | 
			
		||||
                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)))
 | 
			
		||||
                mstore(add(fillResults, 128), mload(add(fillOrderCalldata, 128)))
 | 
			
		||||
            }
 | 
			
		||||
        (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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -99,12 +85,14 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            uint256 makerAssetAcquiredAmount
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        // No fee or percentage fee
 | 
			
		||||
        uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
 | 
			
		||||
 | 
			
		||||
        // No taker fee or percentage fee
 | 
			
		||||
        if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.makerAssetData)) {
 | 
			
		||||
            // Attempt to sell the remaining amount of WETH
 | 
			
		||||
            LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
 | 
			
		||||
                order,
 | 
			
		||||
                remainingTakerAssetFillAmount,
 | 
			
		||||
                remainingTakerAssetFillAmount.safeSub(protocolFee),
 | 
			
		||||
                signature
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
@@ -118,14 +106,13 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            );
 | 
			
		||||
        // WETH fee
 | 
			
		||||
        } else if (order.takerFeeAssetData.equals(order.takerAssetData)) {
 | 
			
		||||
            uint256 protocolFee = tx.gasprice.safeMul(EXCHANGE.protocolFeeMultiplier());
 | 
			
		||||
 | 
			
		||||
            // We will first sell WETH as the takerAsset, then use it to pay the takerFee.
 | 
			
		||||
            // This ensures that we reserve enough to pay the fee.
 | 
			
		||||
            // This ensures that we reserve enough to pay the taker and protocol fees.
 | 
			
		||||
            uint256 takerAssetFillAmount = LibMath.getPartialAmountCeil(
 | 
			
		||||
                order.takerAssetAmount,
 | 
			
		||||
                order.takerAssetAmount.safeAdd(order.takerFee).safeAdd(protocolFee),
 | 
			
		||||
                remainingTakerAssetFillAmount
 | 
			
		||||
                order.takerAssetAmount.safeAdd(order.takerFee),
 | 
			
		||||
                remainingTakerAssetFillAmount.safeSub(protocolFee)
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
 | 
			
		||||
@@ -222,7 +209,7 @@ contract MixinExchangeWrapper is
 | 
			
		||||
            uint256 makerAssetAcquiredAmount
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        // No fee or WETH fee
 | 
			
		||||
        // No taker fee or WETH fee
 | 
			
		||||
        if (order.takerFee == 0 || order.takerFeeAssetData.equals(order.takerAssetData)) {
 | 
			
		||||
            // Calculate the remaining amount of takerAsset to sell
 | 
			
		||||
            uint256 remainingTakerAssetFillAmount = LibMath.getPartialAmountCeil(
 | 
			
		||||
@@ -238,7 +225,7 @@ contract MixinExchangeWrapper is
 | 
			
		||||
                signature
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // WETH is also spent on the taker fee, so we add it here.
 | 
			
		||||
            // WETH is also spent on the protocol and taker fees, so we add it here.
 | 
			
		||||
            wethSpentAmount = singleFillResults.takerAssetFilledAmount.safeAdd(
 | 
			
		||||
                singleFillResults.takerFeePaid
 | 
			
		||||
            ).safeAdd(
 | 
			
		||||
 
 | 
			
		||||
@@ -47,19 +47,19 @@ contract MixinWeth is
 | 
			
		||||
        internal
 | 
			
		||||
    {
 | 
			
		||||
        if (msg.value == 0) {
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.MsgValueCantEqualZeroError());
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.MsgValueCannotEqualZeroError());
 | 
			
		||||
        }
 | 
			
		||||
        ETHER_TOKEN.deposit.value(msg.value)();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Transfers feePercentage of WETH spent on primary orders to feeRecipient.
 | 
			
		||||
    ///      Refunds any excess ETH to msg.sender.
 | 
			
		||||
    /// @param wethSold Amount of WETH sold when filling primary orders.
 | 
			
		||||
    /// @param wethSpent Amount of WETH spent when filling orders.
 | 
			
		||||
    /// @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 ethFee Amount paid to feeRecipient as a percentage fee on the total WETH sold.
 | 
			
		||||
    function _transferEthFeeAndRefund(
 | 
			
		||||
        uint256 wethSold,
 | 
			
		||||
        uint256 wethSpent,
 | 
			
		||||
        uint256 feePercentage,
 | 
			
		||||
        address payable feeRecipient
 | 
			
		||||
    )
 | 
			
		||||
@@ -73,22 +73,22 @@ contract MixinWeth is
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Ensure that no extra WETH owned by this contract has been sold.
 | 
			
		||||
        if (wethSold > msg.value) {
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.OversoldWethError(
 | 
			
		||||
                wethSold,
 | 
			
		||||
        // Ensure that no extra WETH owned by this contract has been spent.
 | 
			
		||||
        if (wethSpent > msg.value) {
 | 
			
		||||
            LibRichErrors.rrevert(LibForwarderRichErrors.OverspentWethError(
 | 
			
		||||
                wethSpent,
 | 
			
		||||
                msg.value
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate amount of WETH that hasn't been sold.
 | 
			
		||||
        uint256 wethRemaining = msg.value.safeSub(wethSold);
 | 
			
		||||
        // Calculate amount of WETH that hasn't been spent.
 | 
			
		||||
        uint256 wethRemaining = msg.value.safeSub(wethSpent);
 | 
			
		||||
 | 
			
		||||
        // Calculate ETH fee to pay to feeRecipient.
 | 
			
		||||
        ethFee = LibMath.getPartialAmountFloor(
 | 
			
		||||
            feePercentage,
 | 
			
		||||
            PERCENTAGE_DENOMINATOR,
 | 
			
		||||
            wethSold
 | 
			
		||||
            wethSpent
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Ensure fee is less than amount of WETH remaining.
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ 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 "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract LibConstants {
 | 
			
		||||
 
 | 
			
		||||
@@ -51,9 +51,9 @@ library LibForwarderRichErrors {
 | 
			
		||||
    bytes4 internal constant INSUFFICIENT_ETH_FOR_FEE_ERROR_SELECTOR =
 | 
			
		||||
        0xecf40fd9;
 | 
			
		||||
 | 
			
		||||
    // bytes4(keccak256("OversoldWethError(uint256,uint256)"))
 | 
			
		||||
    bytes4 internal constant OVERSOLD_WETH_ERROR_SELECTOR =
 | 
			
		||||
        0x5cc555c8;
 | 
			
		||||
    // bytes4(keccak256("OverspentWethError(uint256,uint256)"))
 | 
			
		||||
    bytes4 internal constant OVERSPENT_WETH_ERROR_SELECTOR =
 | 
			
		||||
        0xcdcbed5d;
 | 
			
		||||
 | 
			
		||||
    // bytes4(keccak256("TransferFailedError(bytes)"))
 | 
			
		||||
    bytes4 internal constant TRANSFER_FAILED_ERROR_SELECTOR =
 | 
			
		||||
@@ -63,9 +63,9 @@ library LibForwarderRichErrors {
 | 
			
		||||
    bytes4 internal constant DEFAULT_FUNCTION_WETH_CONTRACT_ONLY_ERROR_SELECTOR =
 | 
			
		||||
        0x08b18698;
 | 
			
		||||
 | 
			
		||||
    // bytes4(keccak256("MsgValueCantEqualZeroError()"))
 | 
			
		||||
    bytes4 internal constant MSG_VALUE_CANT_EQUAL_ZERO_ERROR_SELECTOR =
 | 
			
		||||
        0x1213e1d6;
 | 
			
		||||
    // bytes4(keccak256("MsgValueCannotEqualZeroError()"))
 | 
			
		||||
    bytes4 internal constant MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR =
 | 
			
		||||
        0x8c0e562b;
 | 
			
		||||
 | 
			
		||||
    // bytes4(keccak256("Erc721AmountMustEqualOneError(uint256)"))
 | 
			
		||||
    bytes4 internal constant ERC721_AMOUNT_MUST_EQUAL_ONE_ERROR_SELECTOR =
 | 
			
		||||
@@ -164,8 +164,8 @@ library LibForwarderRichErrors {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function OversoldWethError(
 | 
			
		||||
        uint256 wethSold,
 | 
			
		||||
    function OverspentWethError(
 | 
			
		||||
        uint256 wethSpent,
 | 
			
		||||
        uint256 msgValue
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
@@ -173,8 +173,8 @@ library LibForwarderRichErrors {
 | 
			
		||||
        returns (bytes memory)
 | 
			
		||||
    {
 | 
			
		||||
        return abi.encodeWithSelector(
 | 
			
		||||
            OVERSOLD_WETH_ERROR_SELECTOR,
 | 
			
		||||
            wethSold,
 | 
			
		||||
            OVERSPENT_WETH_ERROR_SELECTOR,
 | 
			
		||||
            wethSpent,
 | 
			
		||||
            msgValue
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@@ -205,12 +205,12 @@ library LibForwarderRichErrors {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function MsgValueCantEqualZeroError()
 | 
			
		||||
    function MsgValueCannotEqualZeroError()
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (bytes memory)
 | 
			
		||||
    {
 | 
			
		||||
        return abi.encodeWithSelector(MSG_VALUE_CANT_EQUAL_ZERO_ERROR_SELECTOR);
 | 
			
		||||
        return abi.encodeWithSelector(MSG_VALUE_CANNOT_EQUAL_ZERO_ERROR_SELECTOR);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function Erc721AmountMustEqualOneError(
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetData.sol";
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IAssetProxy.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 // solhint-disable no-unused-vars
 | 
			
		||||
contract TestProtocolFeeCollector {
 | 
			
		||||
 | 
			
		||||
    address private _wethAddress;
 | 
			
		||||
    address private _wethAssetProxyAddress;
 | 
			
		||||
 | 
			
		||||
    constructor (
 | 
			
		||||
        address wethAddress,
 | 
			
		||||
        address wethAssetProxyAddress
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        _wethAddress = wethAddress;
 | 
			
		||||
        _wethAssetProxyAddress = wethAssetProxyAddress;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Pays a protocol fee in WETH (Forwarder orders will always pay protocol fees in WETH).
 | 
			
		||||
    /// @param makerAddress The address of the order's maker.
 | 
			
		||||
    /// @param payerAddress The address of the protocol fee payer.
 | 
			
		||||
    /// @param protocolFeePaid The protocol fee that should be paid.
 | 
			
		||||
    function payProtocolFee(
 | 
			
		||||
        address makerAddress,
 | 
			
		||||
        address payerAddress,
 | 
			
		||||
        uint256 protocolFeePaid
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
    {
 | 
			
		||||
        assert(msg.value == 0);
 | 
			
		||||
 | 
			
		||||
        IAssetProxy wethAssetProxy = IAssetProxy(_wethAssetProxyAddress);
 | 
			
		||||
        bytes memory wethAssetData = abi.encodeWithSelector(
 | 
			
		||||
            IAssetData(address(0)).ERC20Token.selector,
 | 
			
		||||
            _wethAddress
 | 
			
		||||
        );
 | 
			
		||||
        // Transfer the protocol fee to this address in WETH.
 | 
			
		||||
        wethAssetProxy.transferFrom(
 | 
			
		||||
            wethAssetData,
 | 
			
		||||
            payerAddress,
 | 
			
		||||
            address(this),
 | 
			
		||||
            protocolFeePaid
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "scripts": {
 | 
			
		||||
        "build": "yarn pre_build && tsc -b",
 | 
			
		||||
        "build:ts": "tsc -b",
 | 
			
		||||
        "build:ci": "yarn build",
 | 
			
		||||
        "pre_build": "run-s compile contracts:gen generate_contract_wrappers",
 | 
			
		||||
        "test": "yarn run_mocha",
 | 
			
		||||
@@ -34,7 +35,7 @@
 | 
			
		||||
        "compile:truffle": "truffle compile"
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth).json",
 | 
			
		||||
        "abis": "./generated-artifacts/@(Forwarder|IAssets|IForwarder|IForwarderCore|LibConstants|LibForwarderRichErrors|MixinAssets|MixinExchangeWrapper|MixinForwarderCore|MixinWeth|TestProtocolFeeCollector).json",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import * as MixinAssets from '../generated-artifacts/MixinAssets.json';
 | 
			
		||||
import * as MixinExchangeWrapper from '../generated-artifacts/MixinExchangeWrapper.json';
 | 
			
		||||
import * as MixinForwarderCore from '../generated-artifacts/MixinForwarderCore.json';
 | 
			
		||||
import * as MixinWeth from '../generated-artifacts/MixinWeth.json';
 | 
			
		||||
import * as TestProtocolFeeCollector from '../generated-artifacts/TestProtocolFeeCollector.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    Forwarder: Forwarder as ContractArtifact,
 | 
			
		||||
    MixinAssets: MixinAssets as ContractArtifact,
 | 
			
		||||
@@ -26,4 +27,5 @@ export const artifacts = {
 | 
			
		||||
    IForwarderCore: IForwarderCore as ContractArtifact,
 | 
			
		||||
    LibConstants: LibConstants as ContractArtifact,
 | 
			
		||||
    LibForwarderRichErrors: LibForwarderRichErrors as ContractArtifact,
 | 
			
		||||
    TestProtocolFeeCollector: TestProtocolFeeCollector as ContractArtifact,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -13,3 +13,4 @@ export * from '../generated-wrappers/mixin_assets';
 | 
			
		||||
export * from '../generated-wrappers/mixin_exchange_wrapper';
 | 
			
		||||
export * from '../generated-wrappers/mixin_forwarder_core';
 | 
			
		||||
export * from '../generated-wrappers/mixin_weth';
 | 
			
		||||
export * from '../generated-wrappers/test_protocol_fee_collector';
 | 
			
		||||
 
 | 
			
		||||
@@ -14,21 +14,24 @@ import {
 | 
			
		||||
import { assetDataUtils, ForwarderRevertErrors } from '@0x/order-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { Web3Wrapper } from '@0x/web3-wrapper';
 | 
			
		||||
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
import { artifacts, ForwarderContract, ForwarderTestFactory, ForwarderWrapper } from '../src';
 | 
			
		||||
import {
 | 
			
		||||
    artifacts,
 | 
			
		||||
    ForwarderContract,
 | 
			
		||||
    ForwarderTestFactory,
 | 
			
		||||
    ForwarderWrapper,
 | 
			
		||||
    TestProtocolFeeCollectorContract,
 | 
			
		||||
} from '../src';
 | 
			
		||||
 | 
			
		||||
const DECIMALS_DEFAULT = 18;
 | 
			
		||||
 | 
			
		||||
blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
    let chainId: number;
 | 
			
		||||
    let makerAddress: string;
 | 
			
		||||
    let owner: string;
 | 
			
		||||
    let makerAddress: string;
 | 
			
		||||
    let takerAddress: string;
 | 
			
		||||
    let orderFeeRecipientAddress: string;
 | 
			
		||||
    let forwarderFeeRecipientAddress: string;
 | 
			
		||||
    let defaultMakerAssetAddress: string;
 | 
			
		||||
    let wethAssetData: string;
 | 
			
		||||
 | 
			
		||||
    let weth: DummyERC20TokenContract;
 | 
			
		||||
    let erc20Token: DummyERC20TokenContract;
 | 
			
		||||
@@ -36,22 +39,26 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
    let erc721Token: DummyERC721TokenContract;
 | 
			
		||||
    let forwarderContract: ForwarderContract;
 | 
			
		||||
    let wethContract: WETH9Contract;
 | 
			
		||||
    let exchangeContract: ExchangeContract;
 | 
			
		||||
    let protocolFeeCollector: TestProtocolFeeCollectorContract;
 | 
			
		||||
 | 
			
		||||
    let forwarderWrapper: ForwarderWrapper;
 | 
			
		||||
    let exchangeWrapper: ExchangeWrapper;
 | 
			
		||||
    let erc20Wrapper: ERC20Wrapper;
 | 
			
		||||
 | 
			
		||||
    let orderFactory: OrderFactory;
 | 
			
		||||
    let forwarderTestFactory: ForwarderTestFactory;
 | 
			
		||||
    let erc20Wrapper: ERC20Wrapper;
 | 
			
		||||
    let tx: TransactionReceiptWithDecodedLogs;
 | 
			
		||||
 | 
			
		||||
    let chainId: number;
 | 
			
		||||
    let wethAssetData: string;
 | 
			
		||||
    let erc721MakerAssetIds: BigNumber[];
 | 
			
		||||
    const gasPrice = new BigNumber(constants.DEFAULT_GAS_PRICE);
 | 
			
		||||
 | 
			
		||||
    const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
 | 
			
		||||
    const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150);
 | 
			
		||||
    const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        await env.blockchainLifecycle.startAsync();
 | 
			
		||||
 | 
			
		||||
        chainId = await env.getChainIdAsync();
 | 
			
		||||
 | 
			
		||||
        // Set up addresses
 | 
			
		||||
        const accounts = await env.getAccountAddressesAsync();
 | 
			
		||||
        const usedAddresses = ([
 | 
			
		||||
            owner,
 | 
			
		||||
@@ -61,24 +68,27 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            forwarderFeeRecipientAddress,
 | 
			
		||||
        ] = accounts);
 | 
			
		||||
 | 
			
		||||
        const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
 | 
			
		||||
        erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
 | 
			
		||||
 | 
			
		||||
        const numDummyErc20ToDeploy = 2;
 | 
			
		||||
        [erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(
 | 
			
		||||
            numDummyErc20ToDeploy,
 | 
			
		||||
            constants.DUMMY_TOKEN_DECIMALS,
 | 
			
		||||
        // Set up Exchange
 | 
			
		||||
        chainId = await env.getChainIdAsync();
 | 
			
		||||
        exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            exchangeArtifacts.Exchange,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            {},
 | 
			
		||||
            new BigNumber(chainId),
 | 
			
		||||
        );
 | 
			
		||||
        exchangeWrapper = new ExchangeWrapper(exchangeContract);
 | 
			
		||||
 | 
			
		||||
        // Set up ERC20
 | 
			
		||||
        erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
 | 
			
		||||
        [erc20Token, secondErc20Token] = await erc20Wrapper.deployDummyTokensAsync(2, constants.DUMMY_TOKEN_DECIMALS);
 | 
			
		||||
        const erc20Proxy = await erc20Wrapper.deployProxyAsync();
 | 
			
		||||
        await erc20Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
 | 
			
		||||
        [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
 | 
			
		||||
        const erc721Proxy = await erc721Wrapper.deployProxyAsync();
 | 
			
		||||
        await erc721Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
        const erc721Balances = await erc721Wrapper.getBalancesAsync();
 | 
			
		||||
        erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
 | 
			
		||||
        await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
 | 
			
		||||
        await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
 | 
			
		||||
            from: owner,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Set up WETH
 | 
			
		||||
        wethContract = await WETH9Contract.deployFrom0xArtifactAsync(
 | 
			
		||||
            erc20Artifacts.WETH9,
 | 
			
		||||
            env.provider,
 | 
			
		||||
@@ -86,59 +96,68 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            {},
 | 
			
		||||
        );
 | 
			
		||||
        weth = new DummyERC20TokenContract(wethContract.address, env.provider);
 | 
			
		||||
        erc20Wrapper.addDummyTokenContract(weth);
 | 
			
		||||
 | 
			
		||||
        wethAssetData = assetDataUtils.encodeERC20AssetData(wethContract.address);
 | 
			
		||||
        const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            exchangeArtifacts.Exchange,
 | 
			
		||||
        erc20Wrapper.addDummyTokenContract(weth);
 | 
			
		||||
        await erc20Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
 | 
			
		||||
        // Set up ERC721
 | 
			
		||||
        const erc721Wrapper = new ERC721Wrapper(env.provider, usedAddresses, owner);
 | 
			
		||||
        [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
 | 
			
		||||
        const erc721Proxy = await erc721Wrapper.deployProxyAsync();
 | 
			
		||||
        await erc721Wrapper.setBalancesAndAllowancesAsync();
 | 
			
		||||
        const erc721Balances = await erc721Wrapper.getBalancesAsync();
 | 
			
		||||
        erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
 | 
			
		||||
        await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
 | 
			
		||||
        await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
 | 
			
		||||
            from: owner,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Set up Protocol Fee Collector
 | 
			
		||||
        protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.TestProtocolFeeCollector,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            {},
 | 
			
		||||
            new BigNumber(chainId),
 | 
			
		||||
            wethContract.address,
 | 
			
		||||
            erc20Proxy.address,
 | 
			
		||||
        );
 | 
			
		||||
        exchangeWrapper = new ExchangeWrapper(exchangeInstance, env.provider);
 | 
			
		||||
        await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner);
 | 
			
		||||
        await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner);
 | 
			
		||||
 | 
			
		||||
        await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
 | 
			
		||||
            from: owner,
 | 
			
		||||
        });
 | 
			
		||||
        await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, {
 | 
			
		||||
        await exchangeContract.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER);
 | 
			
		||||
        await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(
 | 
			
		||||
            protocolFeeCollector.address,
 | 
			
		||||
        );
 | 
			
		||||
        await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(protocolFeeCollector.address, {
 | 
			
		||||
            from: owner,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Set defaults
 | 
			
		||||
        defaultMakerAssetAddress = erc20Token.address;
 | 
			
		||||
        const defaultTakerAssetAddress = wethContract.address;
 | 
			
		||||
        const defaultOrderParams = {
 | 
			
		||||
            makerAddress,
 | 
			
		||||
            feeRecipientAddress: orderFeeRecipientAddress,
 | 
			
		||||
            makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
 | 
			
		||||
            takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress),
 | 
			
		||||
            makerAssetAmount: Web3Wrapper.toBaseUnitAmount(200, DECIMALS_DEFAULT),
 | 
			
		||||
            takerAssetAmount: Web3Wrapper.toBaseUnitAmount(10, DECIMALS_DEFAULT),
 | 
			
		||||
            makerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
 | 
			
		||||
            takerFeeAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress),
 | 
			
		||||
            makerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
 | 
			
		||||
            takerFee: Web3Wrapper.toBaseUnitAmount(0, DECIMALS_DEFAULT),
 | 
			
		||||
            exchangeAddress: exchangeInstance.address,
 | 
			
		||||
            exchangeAddress: exchangeContract.address,
 | 
			
		||||
            chainId,
 | 
			
		||||
        };
 | 
			
		||||
        const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
 | 
			
		||||
        orderFactory = new OrderFactory(privateKey, defaultOrderParams);
 | 
			
		||||
 | 
			
		||||
        // Set up Forwarder
 | 
			
		||||
        forwarderContract = await ForwarderContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            artifacts.Forwarder,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            env.txDefaults,
 | 
			
		||||
            {},
 | 
			
		||||
            exchangeInstance.address,
 | 
			
		||||
            exchangeContract.address,
 | 
			
		||||
            wethAssetData,
 | 
			
		||||
        );
 | 
			
		||||
        forwarderWrapper = new ForwarderWrapper(forwarderContract, env.provider);
 | 
			
		||||
 | 
			
		||||
        await forwarderWrapper.approveMakerAssetProxyAsync(defaultOrderParams.makerAssetData, { from: takerAddress });
 | 
			
		||||
        erc20Wrapper.addTokenOwnerAddress(forwarderContract.address);
 | 
			
		||||
 | 
			
		||||
        // Set up factories
 | 
			
		||||
        const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
 | 
			
		||||
        orderFactory = new OrderFactory(privateKey, defaultOrderParams);
 | 
			
		||||
        forwarderTestFactory = new ForwarderTestFactory(
 | 
			
		||||
            exchangeWrapper,
 | 
			
		||||
            forwarderWrapper,
 | 
			
		||||
@@ -146,16 +165,18 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            forwarderContract.address,
 | 
			
		||||
            makerAddress,
 | 
			
		||||
            takerAddress,
 | 
			
		||||
            protocolFeeCollector.address,
 | 
			
		||||
            orderFeeRecipientAddress,
 | 
			
		||||
            forwarderFeeRecipientAddress,
 | 
			
		||||
            weth.address,
 | 
			
		||||
            gasPrice,
 | 
			
		||||
            GAS_PRICE,
 | 
			
		||||
            PROTOCOL_FEE_MULTIPLIER,
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    blockchainTests.resets('constructor', () => {
 | 
			
		||||
        it('should revert if assetProxy is unregistered', async () => {
 | 
			
		||||
            const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
                exchangeArtifacts.Exchange,
 | 
			
		||||
                env.provider,
 | 
			
		||||
                env.txDefaults,
 | 
			
		||||
@@ -168,7 +189,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
                env.provider,
 | 
			
		||||
                env.txDefaults,
 | 
			
		||||
                {},
 | 
			
		||||
                exchangeInstance.address,
 | 
			
		||||
                exchange.address,
 | 
			
		||||
                wethAssetData,
 | 
			
		||||
            ) as any) as sendTransactionResult;
 | 
			
		||||
 | 
			
		||||
@@ -223,7 +244,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
 | 
			
		||||
            // Execute test case
 | 
			
		||||
            tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
 | 
			
		||||
            const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
 | 
			
		||||
                value: ethValue,
 | 
			
		||||
                from: takerAddress,
 | 
			
		||||
            });
 | 
			
		||||
@@ -231,7 +252,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
            const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
 | 
			
		||||
            const newBalances = await erc20Wrapper.getBalancesAsync();
 | 
			
		||||
            const totalEthSpent = gasPrice.times(tx.gasUsed);
 | 
			
		||||
            const totalEthSpent = GAS_PRICE.times(tx.gasUsed);
 | 
			
		||||
 | 
			
		||||
            // Validate test case
 | 
			
		||||
            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
 | 
			
		||||
@@ -273,15 +294,15 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
        });
 | 
			
		||||
        it('should refund remaining ETH if amount is greater than takerAssetAmount', async () => {
 | 
			
		||||
            const order = await orderFactory.newSignedOrderAsync();
 | 
			
		||||
            const ethValue = order.takerAssetAmount.plus(2);
 | 
			
		||||
            const ethValue = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(2);
 | 
			
		||||
            const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
 | 
			
		||||
            tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
 | 
			
		||||
            const tx = await forwarderWrapper.marketSellOrdersWithEthAsync([order], {
 | 
			
		||||
                value: ethValue,
 | 
			
		||||
                from: takerAddress,
 | 
			
		||||
            });
 | 
			
		||||
            const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
            const totalEthSpent = order.takerAssetAmount.plus(gasPrice.times(tx.gasUsed));
 | 
			
		||||
            const totalEthSpent = order.takerAssetAmount.plus(PROTOCOL_FEE).plus(GAS_PRICE.times(tx.gasUsed));
 | 
			
		||||
 | 
			
		||||
            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
 | 
			
		||||
        });
 | 
			
		||||
@@ -527,7 +548,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const fillableOrder = await orderFactory.newSignedOrderAsync();
 | 
			
		||||
            await forwarderTestFactory.marketBuyTestAsync([cancelledOrder, fillableOrder], 1.5, erc20Token);
 | 
			
		||||
        });
 | 
			
		||||
        it('Should buy slightly greater MakerAsset when exchange rate is rounded', async () => {
 | 
			
		||||
        it('Should buy slightly greater makerAsset when exchange rate is rounded', async () => {
 | 
			
		||||
            // The 0x Protocol contracts round the exchange rate in favor of the Maker.
 | 
			
		||||
            // In this case, the taker must round up how much they're going to spend, which
 | 
			
		||||
            // in turn increases the amount of MakerAsset being purchased.
 | 
			
		||||
@@ -553,13 +574,14 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            });
 | 
			
		||||
            const desiredMakerAssetFillAmount = new BigNumber('5');
 | 
			
		||||
            const makerAssetFillAmount = new BigNumber('6');
 | 
			
		||||
            const ethValue = new BigNumber('4');
 | 
			
		||||
            const primaryTakerAssetFillAmount = new BigNumber('4');
 | 
			
		||||
            const ethValue = primaryTakerAssetFillAmount.plus(PROTOCOL_FEE);
 | 
			
		||||
 | 
			
		||||
            const erc20Balances = await erc20Wrapper.getBalancesAsync();
 | 
			
		||||
            const takerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
 | 
			
		||||
            // Execute test case
 | 
			
		||||
            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
 | 
			
		||||
            const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
 | 
			
		||||
                value: ethValue,
 | 
			
		||||
                from: takerAddress,
 | 
			
		||||
            });
 | 
			
		||||
@@ -567,8 +589,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const takerEthBalanceAfter = await env.web3Wrapper.getBalanceInWeiAsync(takerAddress);
 | 
			
		||||
            const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
 | 
			
		||||
            const newBalances = await erc20Wrapper.getBalancesAsync();
 | 
			
		||||
            const primaryTakerAssetFillAmount = ethValue;
 | 
			
		||||
            const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
 | 
			
		||||
            const totalEthSpent = ethValue.plus(GAS_PRICE.times(tx.gasUsed));
 | 
			
		||||
            // Validate test case
 | 
			
		||||
            expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
 | 
			
		||||
            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
 | 
			
		||||
@@ -588,6 +609,8 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
        });
 | 
			
		||||
        it('Should buy slightly greater MakerAsset when exchange rate is rounded (Regression Test)', async () => {
 | 
			
		||||
            // Disable protocol fees for regression test
 | 
			
		||||
            await exchangeContract.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(constants.NULL_ADDRESS);
 | 
			
		||||
            // Order taken from a transaction on mainnet that failed due to a rounding error.
 | 
			
		||||
            const order = await orderFactory.newSignedOrderAsync({
 | 
			
		||||
                makerAssetAmount: new BigNumber('268166666666666666666'),
 | 
			
		||||
@@ -608,7 +631,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
                .times(order.makerAssetAmount)
 | 
			
		||||
                .dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
            // Execute test case
 | 
			
		||||
            tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
 | 
			
		||||
            const tx = await forwarderWrapper.marketBuyOrdersWithEthAsync([order], desiredMakerAssetFillAmount, {
 | 
			
		||||
                value: ethValue,
 | 
			
		||||
                from: takerAddress,
 | 
			
		||||
            });
 | 
			
		||||
@@ -617,7 +640,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const forwarderEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(forwarderContract.address);
 | 
			
		||||
            const newBalances = await erc20Wrapper.getBalancesAsync();
 | 
			
		||||
            const primaryTakerAssetFillAmount = ethValue;
 | 
			
		||||
            const totalEthSpent = primaryTakerAssetFillAmount.plus(gasPrice.times(tx.gasUsed));
 | 
			
		||||
            const totalEthSpent = primaryTakerAssetFillAmount.plus(GAS_PRICE.times(tx.gasUsed));
 | 
			
		||||
            // Validate test case
 | 
			
		||||
            expect(makerAssetFillAmount).to.be.bignumber.greaterThan(desiredMakerAssetFillAmount);
 | 
			
		||||
            expect(takerEthBalanceAfter).to.be.bignumber.equal(takerEthBalanceBefore.minus(totalEthSpent));
 | 
			
		||||
@@ -661,7 +684,7 @@ blockchainTests(ContractName.Forwarder, env => {
 | 
			
		||||
            const order = await orderFactory.newSignedOrderAsync();
 | 
			
		||||
            const forwarderFeePercentage = new BigNumber(2);
 | 
			
		||||
            const ethFee = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
                order.takerAssetAmount.times(0.5),
 | 
			
		||||
                order.takerAssetAmount.times(0.5).plus(PROTOCOL_FEE),
 | 
			
		||||
                forwarderFeePercentage,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,118 +2,31 @@ import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
 | 
			
		||||
import { DummyERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
 | 
			
		||||
import { ExchangeWrapper } from '@0x/contracts-exchange';
 | 
			
		||||
import { chaiSetup, constants, ERC20BalancesByOwner, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
 | 
			
		||||
import { constants, ERC20BalancesByOwner, expect, OrderStatus, web3Wrapper } from '@0x/contracts-test-utils';
 | 
			
		||||
import { OrderInfo, SignedOrder } from '@0x/types';
 | 
			
		||||
import { BigNumber, RevertError } from '@0x/utils';
 | 
			
		||||
import * as chai from 'chai';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { ForwarderWrapper } from './forwarder_wrapper';
 | 
			
		||||
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
 | 
			
		||||
// Necessary bookkeeping to validate Forwarder results
 | 
			
		||||
interface ForwarderFillState {
 | 
			
		||||
    takerAssetFillAmount: BigNumber;
 | 
			
		||||
    makerAssetFillAmount: BigNumber;
 | 
			
		||||
    protocolFees: BigNumber;
 | 
			
		||||
    wethFees: BigNumber;
 | 
			
		||||
    percentageFees: BigNumber;
 | 
			
		||||
    maxOversoldWeth: BigNumber;
 | 
			
		||||
    maxOverboughtMakerAsset: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Simulates filling some orders via the Forwarder contract. For example, if
 | 
			
		||||
// orders = [A, B, C, D] and fractionalNumberOfOrdersToFill = 2.3, then
 | 
			
		||||
// we simulate A and B being completely filled, and 0.3 * C being filled.
 | 
			
		||||
function computeExpectedResults(
 | 
			
		||||
    orders: SignedOrder[],
 | 
			
		||||
    ordersInfoBefore: OrderInfo[],
 | 
			
		||||
    fractionalNumberOfOrdersToFill: number,
 | 
			
		||||
): ForwarderFillState {
 | 
			
		||||
    const currentState = {
 | 
			
		||||
        takerAssetFillAmount: constants.ZERO_AMOUNT,
 | 
			
		||||
        makerAssetFillAmount: constants.ZERO_AMOUNT,
 | 
			
		||||
        wethFees: constants.ZERO_AMOUNT,
 | 
			
		||||
        percentageFees: constants.ZERO_AMOUNT,
 | 
			
		||||
        maxOversoldWeth: constants.ZERO_AMOUNT,
 | 
			
		||||
        maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
 | 
			
		||||
    };
 | 
			
		||||
    let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
 | 
			
		||||
 | 
			
		||||
    for (const [i, order] of orders.entries()) {
 | 
			
		||||
        if (remainingOrdersToFill === 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (ordersInfoBefore[i].orderStatus !== OrderStatus.Fillable) {
 | 
			
		||||
            // If the order is not fillable, skip over it but still count it towards fractionalNumberOfOrdersToFill
 | 
			
		||||
            remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let makerAssetAmount;
 | 
			
		||||
        let takerAssetAmount;
 | 
			
		||||
        let takerFee;
 | 
			
		||||
        if (remainingOrdersToFill < 1) {
 | 
			
		||||
            makerAssetAmount = order.makerAssetAmount.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
            takerAssetAmount = order.takerAssetAmount.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
            takerFee = order.takerFee.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
 | 
			
		||||
            // Up to 1 wei worth of WETH will be oversold on the last order due to rounding
 | 
			
		||||
            currentState.maxOversoldWeth = new BigNumber(1);
 | 
			
		||||
            // Equivalently, up to 1 wei worth of maker asset will be overbought
 | 
			
		||||
            currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
 | 
			
		||||
                .times(order.makerAssetAmount)
 | 
			
		||||
                .dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
        } else {
 | 
			
		||||
            makerAssetAmount = order.makerAssetAmount;
 | 
			
		||||
            takerAssetAmount = order.takerAssetAmount;
 | 
			
		||||
            takerFee = order.takerFee;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Accounting for partially filled orders
 | 
			
		||||
        // As with unfillable orders, these still count as 1 towards fractionalNumberOfOrdersToFill
 | 
			
		||||
        const takerAssetFilled = ordersInfoBefore[i].orderTakerAssetFilledAmount;
 | 
			
		||||
        const makerAssetFilled = takerAssetFilled
 | 
			
		||||
            .times(order.makerAssetAmount)
 | 
			
		||||
            .dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
        takerAssetAmount = BigNumber.max(takerAssetAmount.minus(takerAssetFilled), constants.ZERO_AMOUNT);
 | 
			
		||||
        makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
 | 
			
		||||
 | 
			
		||||
        currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
 | 
			
		||||
        currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
 | 
			
		||||
 | 
			
		||||
        if (order.takerFeeAssetData === order.makerAssetData) {
 | 
			
		||||
            currentState.percentageFees = currentState.percentageFees.plus(takerFee);
 | 
			
		||||
        } else if (order.takerFeeAssetData === order.takerAssetData) {
 | 
			
		||||
            currentState.wethFees = currentState.wethFees.plus(takerFee);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return currentState;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Since bignumber is not compatible with chai's within
 | 
			
		||||
function expectBalanceWithin(balance: BigNumber, low: BigNumber, high: BigNumber): void {
 | 
			
		||||
    expect(balance).to.be.bignumber.gte(low);
 | 
			
		||||
    expect(balance).to.be.bignumber.lte(high);
 | 
			
		||||
function expectBalanceWithin(balance: BigNumber, low: BigNumber, high: BigNumber, message?: string): void {
 | 
			
		||||
    expect(balance, message).to.be.bignumber.gte(low);
 | 
			
		||||
    expect(balance, message).to.be.bignumber.lte(high);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ForwarderTestFactory {
 | 
			
		||||
    private readonly _exchangeWrapper: ExchangeWrapper;
 | 
			
		||||
    private readonly _forwarderWrapper: ForwarderWrapper;
 | 
			
		||||
    private readonly _erc20Wrapper: ERC20Wrapper;
 | 
			
		||||
    private readonly _forwarderAddress: string;
 | 
			
		||||
    private readonly _makerAddress: string;
 | 
			
		||||
    private readonly _takerAddress: string;
 | 
			
		||||
    private readonly _orderFeeRecipientAddress: string;
 | 
			
		||||
    private readonly _forwarderFeeRecipientAddress: string;
 | 
			
		||||
    private readonly _wethAddress: string;
 | 
			
		||||
    private readonly _gasPrice: BigNumber;
 | 
			
		||||
 | 
			
		||||
    public static getPercentageOfValue(value: BigNumber, percentage: BigNumber): BigNumber {
 | 
			
		||||
        const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100);
 | 
			
		||||
        const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR);
 | 
			
		||||
@@ -121,28 +34,19 @@ export class ForwarderTestFactory {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        exchangeWrapper: ExchangeWrapper,
 | 
			
		||||
        forwarderWrapper: ForwarderWrapper,
 | 
			
		||||
        erc20Wrapper: ERC20Wrapper,
 | 
			
		||||
        forwarderAddress: string,
 | 
			
		||||
        makerAddress: string,
 | 
			
		||||
        takerAddress: string,
 | 
			
		||||
        orderFeeRecipientAddress: string,
 | 
			
		||||
        forwarderFeeRecipientAddress: string,
 | 
			
		||||
        wethAddress: string,
 | 
			
		||||
        gasPrice: BigNumber,
 | 
			
		||||
    ) {
 | 
			
		||||
        this._exchangeWrapper = exchangeWrapper;
 | 
			
		||||
        this._forwarderWrapper = forwarderWrapper;
 | 
			
		||||
        this._erc20Wrapper = erc20Wrapper;
 | 
			
		||||
        this._forwarderAddress = forwarderAddress;
 | 
			
		||||
        this._makerAddress = makerAddress;
 | 
			
		||||
        this._takerAddress = takerAddress;
 | 
			
		||||
        this._orderFeeRecipientAddress = orderFeeRecipientAddress;
 | 
			
		||||
        this._forwarderFeeRecipientAddress = forwarderFeeRecipientAddress;
 | 
			
		||||
        this._wethAddress = wethAddress;
 | 
			
		||||
        this._gasPrice = gasPrice;
 | 
			
		||||
    }
 | 
			
		||||
        private readonly _exchangeWrapper: ExchangeWrapper,
 | 
			
		||||
        private readonly _forwarderWrapper: ForwarderWrapper,
 | 
			
		||||
        private readonly _erc20Wrapper: ERC20Wrapper,
 | 
			
		||||
        private readonly _forwarderAddress: string,
 | 
			
		||||
        private readonly _makerAddress: string,
 | 
			
		||||
        private readonly _takerAddress: string,
 | 
			
		||||
        private readonly _protocolFeeCollectorAddress: string,
 | 
			
		||||
        private readonly _orderFeeRecipientAddress: string,
 | 
			
		||||
        private readonly _forwarderFeeRecipientAddress: string,
 | 
			
		||||
        private readonly _wethAddress: string,
 | 
			
		||||
        private readonly _gasPrice: BigNumber,
 | 
			
		||||
        private readonly _protocolFeeMultiplier: BigNumber,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public async marketBuyTestAsync(
 | 
			
		||||
        orders: SignedOrder[],
 | 
			
		||||
@@ -167,22 +71,18 @@ export class ForwarderTestFactory {
 | 
			
		||||
        const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
 | 
			
		||||
        const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
 | 
			
		||||
 | 
			
		||||
        const expectedResults = computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
 | 
			
		||||
        const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
            expectedResults.takerAssetFillAmount,
 | 
			
		||||
            forwarderFeePercentage,
 | 
			
		||||
        );
 | 
			
		||||
        const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
 | 
			
		||||
        const wethSpent = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.protocolFees)
 | 
			
		||||
            .plus(expectedResults.wethFees)
 | 
			
		||||
            .plus(expectedResults.maxOversoldWeth);
 | 
			
		||||
        const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
 | 
			
		||||
        const ethValue = wethSpent.plus(ethSpentOnForwarderFee).plus(ethValueAdjustment);
 | 
			
		||||
 | 
			
		||||
        const feePercentage = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
            constants.PERCENTAGE_DENOMINATOR,
 | 
			
		||||
            forwarderFeePercentage,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const ethValue = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.wethFees)
 | 
			
		||||
            .plus(expectedResults.maxOversoldWeth)
 | 
			
		||||
            .plus(ethSpentOnForwarderFee)
 | 
			
		||||
            .plus(ethValueAdjustment);
 | 
			
		||||
 | 
			
		||||
        const tx = this._forwarderWrapper.marketBuyOrdersWithEthAsync(
 | 
			
		||||
            orders,
 | 
			
		||||
            expectedResults.makerAssetFillAmount.minus(expectedResults.percentageFees),
 | 
			
		||||
@@ -240,21 +140,20 @@ export class ForwarderTestFactory {
 | 
			
		||||
        const ordersInfoBefore = await Promise.all(orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)));
 | 
			
		||||
        const orderStatusesBefore = ordersInfoBefore.map(orderInfo => orderInfo.orderStatus);
 | 
			
		||||
 | 
			
		||||
        const expectedResults = computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
 | 
			
		||||
        const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
            expectedResults.takerAssetFillAmount,
 | 
			
		||||
            forwarderFeePercentage,
 | 
			
		||||
        );
 | 
			
		||||
        const expectedResults = this._computeExpectedResults(orders, ordersInfoBefore, fractionalNumberOfOrdersToFill);
 | 
			
		||||
        const wethSpent = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.protocolFees)
 | 
			
		||||
            .plus(expectedResults.wethFees)
 | 
			
		||||
            .plus(expectedResults.maxOversoldWeth);
 | 
			
		||||
 | 
			
		||||
        const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(wethSpent, forwarderFeePercentage);
 | 
			
		||||
        const ethValue = wethSpent.plus(ethSpentOnForwarderFee);
 | 
			
		||||
 | 
			
		||||
        const feePercentage = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
            constants.PERCENTAGE_DENOMINATOR,
 | 
			
		||||
            forwarderFeePercentage,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const ethValue = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.wethFees)
 | 
			
		||||
            .plus(expectedResults.maxOversoldWeth)
 | 
			
		||||
            .plus(ethSpentOnForwarderFee);
 | 
			
		||||
 | 
			
		||||
        const tx = this._forwarderWrapper.marketSellOrdersWithEthAsync(
 | 
			
		||||
            orders,
 | 
			
		||||
            {
 | 
			
		||||
@@ -268,10 +167,9 @@ export class ForwarderTestFactory {
 | 
			
		||||
            await expect(tx).to.revertWith(options.revertError);
 | 
			
		||||
        } else {
 | 
			
		||||
            const gasUsed = (await tx).gasUsed;
 | 
			
		||||
            const ordersInfoAfter = await Promise.all(
 | 
			
		||||
                orders.map(order => this._exchangeWrapper.getOrderInfoAsync(order)),
 | 
			
		||||
            const orderStatusesAfter = await Promise.all(
 | 
			
		||||
                orders.map(async order => (await this._exchangeWrapper.getOrderInfoAsync(order)).orderStatus),
 | 
			
		||||
            );
 | 
			
		||||
            const orderStatusesAfter = ordersInfoAfter.map(orderInfo => orderInfo.orderStatus);
 | 
			
		||||
 | 
			
		||||
            await this._checkResultsAsync(
 | 
			
		||||
                fractionalNumberOfOrdersToFill,
 | 
			
		||||
@@ -303,6 +201,7 @@ export class ForwarderTestFactory {
 | 
			
		||||
                .minus(expectedResults.makerAssetFillAmount)
 | 
			
		||||
                .minus(expectedResults.maxOverboughtMakerAsset),
 | 
			
		||||
            oldBalances[this._makerAddress][makerAssetAddress].minus(expectedResults.makerAssetFillAmount),
 | 
			
		||||
            'Maker makerAsset balance',
 | 
			
		||||
        );
 | 
			
		||||
        expectBalanceWithin(
 | 
			
		||||
            newBalances[this._takerAddress][makerAssetAddress],
 | 
			
		||||
@@ -313,11 +212,18 @@ export class ForwarderTestFactory {
 | 
			
		||||
                .plus(expectedResults.makerAssetFillAmount)
 | 
			
		||||
                .minus(expectedResults.percentageFees)
 | 
			
		||||
                .plus(expectedResults.maxOverboughtMakerAsset),
 | 
			
		||||
            'Taker makerAsset balance',
 | 
			
		||||
        );
 | 
			
		||||
        expect(newBalances[this._orderFeeRecipientAddress][makerAssetAddress]).to.be.bignumber.equal(
 | 
			
		||||
        expect(
 | 
			
		||||
            newBalances[this._orderFeeRecipientAddress][makerAssetAddress],
 | 
			
		||||
            'Order fee recipient makerAsset balance',
 | 
			
		||||
        ).to.be.bignumber.equal(
 | 
			
		||||
            oldBalances[this._orderFeeRecipientAddress][makerAssetAddress].plus(expectedResults.percentageFees),
 | 
			
		||||
        );
 | 
			
		||||
        expect(newBalances[this._forwarderAddress][makerAssetAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
        expect(
 | 
			
		||||
            newBalances[this._forwarderAddress][makerAssetAddress],
 | 
			
		||||
            'Forwarder contract makerAsset balance',
 | 
			
		||||
        ).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async _checkResultsAsync(
 | 
			
		||||
@@ -340,18 +246,17 @@ export class ForwarderTestFactory {
 | 
			
		||||
            if (fractionalNumberOfOrdersToFill >= i + 1 && orderStatusesBefore[i] === OrderStatus.Fillable) {
 | 
			
		||||
                expectedOrderStatus = OrderStatus.FullyFilled;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            expect(orderStatus).to.equal(expectedOrderStatus);
 | 
			
		||||
            expect(orderStatus, ` Order ${i} status`).to.equal(expectedOrderStatus);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const wethSpent = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.protocolFees)
 | 
			
		||||
            .plus(expectedResults.wethFees);
 | 
			
		||||
        const ethSpentOnForwarderFee = ForwarderTestFactory.getPercentageOfValue(
 | 
			
		||||
            expectedResults.takerAssetFillAmount,
 | 
			
		||||
            wethSpent,
 | 
			
		||||
            options.forwarderFeePercentage || constants.ZERO_AMOUNT,
 | 
			
		||||
        );
 | 
			
		||||
        const totalEthSpent = expectedResults.takerAssetFillAmount
 | 
			
		||||
            .plus(expectedResults.wethFees)
 | 
			
		||||
            .plus(ethSpentOnForwarderFee)
 | 
			
		||||
            .plus(this._gasPrice.times(gasUsed));
 | 
			
		||||
        const totalEthSpent = wethSpent.plus(ethSpentOnForwarderFee).plus(this._gasPrice.times(gasUsed));
 | 
			
		||||
 | 
			
		||||
        const takerEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(this._takerAddress);
 | 
			
		||||
        const forwarderEthBalance = await web3Wrapper.getBalanceInWeiAsync(this._forwarderAddress);
 | 
			
		||||
@@ -361,12 +266,13 @@ export class ForwarderTestFactory {
 | 
			
		||||
            takerEthBalanceAfter,
 | 
			
		||||
            takerEthBalanceBefore.minus(totalEthSpent).minus(expectedResults.maxOversoldWeth),
 | 
			
		||||
            takerEthBalanceBefore.minus(totalEthSpent),
 | 
			
		||||
            'Taker ETH balance',
 | 
			
		||||
        );
 | 
			
		||||
        if (options.forwarderFeeRecipientEthBalanceBefore !== undefined) {
 | 
			
		||||
            const fowarderFeeRecipientEthBalanceAfter = await web3Wrapper.getBalanceInWeiAsync(
 | 
			
		||||
                this._forwarderFeeRecipientAddress,
 | 
			
		||||
            );
 | 
			
		||||
            expect(fowarderFeeRecipientEthBalanceAfter).to.be.bignumber.equal(
 | 
			
		||||
            expect(fowarderFeeRecipientEthBalanceAfter, 'Forwarder fee recipient ETH balance').to.be.bignumber.equal(
 | 
			
		||||
                options.forwarderFeeRecipientEthBalanceBefore.plus(ethSpentOnForwarderFee),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
@@ -375,7 +281,7 @@ export class ForwarderTestFactory {
 | 
			
		||||
            this._checkErc20Balances(erc20Balances, newBalances, expectedResults, makerAssetContract);
 | 
			
		||||
        } else if (options.makerAssetId !== undefined) {
 | 
			
		||||
            const newOwner = await makerAssetContract.ownerOf.callAsync(options.makerAssetId);
 | 
			
		||||
            expect(newOwner).to.be.bignumber.equal(this._takerAddress);
 | 
			
		||||
            expect(newOwner, 'New ERC721 owner').to.be.bignumber.equal(this._takerAddress);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        expectBalanceWithin(
 | 
			
		||||
@@ -384,12 +290,97 @@ export class ForwarderTestFactory {
 | 
			
		||||
            erc20Balances[this._makerAddress][this._wethAddress]
 | 
			
		||||
                .plus(expectedResults.takerAssetFillAmount)
 | 
			
		||||
                .plus(expectedResults.maxOversoldWeth),
 | 
			
		||||
            'Maker WETH balance',
 | 
			
		||||
        );
 | 
			
		||||
        expect(newBalances[this._orderFeeRecipientAddress][this._wethAddress]).to.be.bignumber.equal(
 | 
			
		||||
        expect(
 | 
			
		||||
            newBalances[this._orderFeeRecipientAddress][this._wethAddress],
 | 
			
		||||
            'Order fee recipient WETH balance',
 | 
			
		||||
        ).to.be.bignumber.equal(
 | 
			
		||||
            erc20Balances[this._orderFeeRecipientAddress][this._wethAddress].plus(expectedResults.wethFees),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(newBalances[this._forwarderAddress][this._wethAddress]).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
        expect(
 | 
			
		||||
            newBalances[this._forwarderAddress][this._wethAddress],
 | 
			
		||||
            'Forwarder contract WETH balance',
 | 
			
		||||
        ).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
        expect(forwarderEthBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Simulates filling some orders via the Forwarder contract. For example, if
 | 
			
		||||
    // orders = [A, B, C, D] and fractionalNumberOfOrdersToFill = 2.3, then
 | 
			
		||||
    // we simulate A and B being completely filled, and 0.3 * C being filled.
 | 
			
		||||
    private _computeExpectedResults(
 | 
			
		||||
        orders: SignedOrder[],
 | 
			
		||||
        ordersInfoBefore: OrderInfo[],
 | 
			
		||||
        fractionalNumberOfOrdersToFill: number,
 | 
			
		||||
    ): ForwarderFillState {
 | 
			
		||||
        const currentState = {
 | 
			
		||||
            takerAssetFillAmount: constants.ZERO_AMOUNT,
 | 
			
		||||
            makerAssetFillAmount: constants.ZERO_AMOUNT,
 | 
			
		||||
            protocolFees: constants.ZERO_AMOUNT,
 | 
			
		||||
            wethFees: constants.ZERO_AMOUNT,
 | 
			
		||||
            percentageFees: constants.ZERO_AMOUNT,
 | 
			
		||||
            maxOversoldWeth: constants.ZERO_AMOUNT,
 | 
			
		||||
            maxOverboughtMakerAsset: constants.ZERO_AMOUNT,
 | 
			
		||||
        };
 | 
			
		||||
        let remainingOrdersToFill = fractionalNumberOfOrdersToFill;
 | 
			
		||||
 | 
			
		||||
        for (const [i, order] of orders.entries()) {
 | 
			
		||||
            if (remainingOrdersToFill === 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ordersInfoBefore[i].orderStatus !== OrderStatus.Fillable) {
 | 
			
		||||
                // If the order is not fillable, skip over it but still count it towards fractionalNumberOfOrdersToFill
 | 
			
		||||
                remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let makerAssetAmount;
 | 
			
		||||
            let takerAssetAmount;
 | 
			
		||||
            let takerFee;
 | 
			
		||||
            if (remainingOrdersToFill < 1) {
 | 
			
		||||
                makerAssetAmount = order.makerAssetAmount.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
                takerAssetAmount = order.takerAssetAmount.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
                takerFee = order.takerFee.times(remainingOrdersToFill).integerValue();
 | 
			
		||||
 | 
			
		||||
                // Up to 1 wei worth of WETH will be oversold on the last order due to rounding
 | 
			
		||||
                currentState.maxOversoldWeth = new BigNumber(1);
 | 
			
		||||
                // Equivalently, up to 1 wei worth of maker asset will be overbought
 | 
			
		||||
                currentState.maxOverboughtMakerAsset = currentState.maxOversoldWeth
 | 
			
		||||
                    .times(order.makerAssetAmount)
 | 
			
		||||
                    .dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
            } else {
 | 
			
		||||
                makerAssetAmount = order.makerAssetAmount;
 | 
			
		||||
                takerAssetAmount = order.takerAssetAmount;
 | 
			
		||||
                takerFee = order.takerFee;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Accounting for partially filled orders
 | 
			
		||||
            // As with unfillable orders, these still count as 1 towards fractionalNumberOfOrdersToFill
 | 
			
		||||
            const takerAssetFilled = ordersInfoBefore[i].orderTakerAssetFilledAmount;
 | 
			
		||||
            const makerAssetFilled = takerAssetFilled
 | 
			
		||||
                .times(order.makerAssetAmount)
 | 
			
		||||
                .dividedToIntegerBy(order.takerAssetAmount);
 | 
			
		||||
            takerAssetAmount = BigNumber.max(takerAssetAmount.minus(takerAssetFilled), constants.ZERO_AMOUNT);
 | 
			
		||||
            makerAssetAmount = BigNumber.max(makerAssetAmount.minus(makerAssetFilled), constants.ZERO_AMOUNT);
 | 
			
		||||
 | 
			
		||||
            currentState.takerAssetFillAmount = currentState.takerAssetFillAmount.plus(takerAssetAmount);
 | 
			
		||||
            currentState.makerAssetFillAmount = currentState.makerAssetFillAmount.plus(makerAssetAmount);
 | 
			
		||||
 | 
			
		||||
            if (this._protocolFeeCollectorAddress !== constants.NULL_ADDRESS) {
 | 
			
		||||
                currentState.protocolFees = currentState.protocolFees.plus(
 | 
			
		||||
                    this._gasPrice.times(this._protocolFeeMultiplier),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            if (order.takerFeeAssetData === order.makerAssetData) {
 | 
			
		||||
                currentState.percentageFees = currentState.percentageFees.plus(takerFee);
 | 
			
		||||
            } else if (order.takerFeeAssetData === order.takerAssetData) {
 | 
			
		||||
                currentState.wethFees = currentState.wethFees.plus(takerFee);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            remainingOrdersToFill = Math.max(remainingOrdersToFill - 1, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return currentState;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,8 @@
 | 
			
		||||
        "generated-artifacts/MixinAssets.json",
 | 
			
		||||
        "generated-artifacts/MixinExchangeWrapper.json",
 | 
			
		||||
        "generated-artifacts/MixinForwarderCore.json",
 | 
			
		||||
        "generated-artifacts/MixinWeth.json"
 | 
			
		||||
        "generated-artifacts/MixinWeth.json",
 | 
			
		||||
        "generated-artifacts/TestProtocolFeeCollector.json"
 | 
			
		||||
    ],
 | 
			
		||||
    "exclude": ["./deploy/solc/solc_bin"]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user