Compare commits
	
		
			11 Commits
		
	
	
		
			@0x/contra
			...
			poc/swap-r
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7972c2ce4e | ||
| 
						 | 
					173d4ce648 | ||
| 
						 | 
					59542f0585 | ||
| 
						 | 
					446ef9660e | ||
| 
						 | 
					9de1f0263a | ||
| 
						 | 
					835ee4e8de | ||
| 
						 | 
					63ec42303f | ||
| 
						 | 
					f789aebddc | ||
| 
						 | 
					efd83be779 | ||
| 
						 | 
					603bc1d51c | ||
| 
						 | 
					32a930a7fc | 
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@0x/contracts-integrations",
 | 
			
		||||
    "version": "2.7.51",
 | 
			
		||||
    "version": "2.7.53",
 | 
			
		||||
    "private": true,
 | 
			
		||||
    "engines": {
 | 
			
		||||
        "node": ">=6.12"
 | 
			
		||||
@@ -93,7 +93,7 @@
 | 
			
		||||
        "typescript": "4.2.2"
 | 
			
		||||
    },
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@0x/asset-swapper": "^6.18.0",
 | 
			
		||||
        "@0x/asset-swapper": "^6.18.2",
 | 
			
		||||
        "@0x/base-contract": "^6.4.0",
 | 
			
		||||
        "@0x/contracts-asset-proxy": "^3.7.16",
 | 
			
		||||
        "@0x/contracts-erc1155": "^2.1.34",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "0.27.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Refactor Mixins which use WETH to also have an Internal variant, allowing WETH to be passed in for SwapRevertSampler",
 | 
			
		||||
                "pr": 245
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "0.26.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,13 @@ interface IBancorNetwork {
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        returns (uint256);
 | 
			
		||||
    function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory);
 | 
			
		||||
    function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IBancorRegistry {
 | 
			
		||||
    function getAddress(bytes32 _contractName) external view returns (address);
 | 
			
		||||
    function BANCOR_NETWORK() external view returns (bytes32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -62,6 +69,18 @@ contract MixinBancor {
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeBancorInternal(WETH, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeBancorInternal(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        // Decode the bridge data.
 | 
			
		||||
        IBancorNetwork bancorNetworkAddress;
 | 
			
		||||
@@ -79,7 +98,7 @@ contract MixinBancor {
 | 
			
		||||
        require(path.length >= 2, "MixinBancor/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
 | 
			
		||||
        require(
 | 
			
		||||
            path[path.length - 1] == buyToken ||
 | 
			
		||||
            (path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == WETH),
 | 
			
		||||
            (path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == weth),
 | 
			
		||||
            "MixinBancor/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +107,7 @@ contract MixinBancor {
 | 
			
		||||
        // The Bancor path will have ETH as the 0xeee address
 | 
			
		||||
        // Bancor expects to be paid in ETH not WETH
 | 
			
		||||
        if (path[0] == BANCOR_ETH_ADDRESS) {
 | 
			
		||||
            WETH.withdraw(sellAmount);
 | 
			
		||||
            weth.withdraw(sellAmount);
 | 
			
		||||
            payableAmount = sellAmount;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Grant an allowance to the Bancor Network.
 | 
			
		||||
@@ -109,7 +128,7 @@ contract MixinBancor {
 | 
			
		||||
            0 // affiliateFee; no fee paid
 | 
			
		||||
        );
 | 
			
		||||
        if (path[path.length - 1] == BANCOR_ETH_ADDRESS) {
 | 
			
		||||
            WETH.deposit{value: boughtAmount}();
 | 
			
		||||
            weth.deposit{value: boughtAmount}();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return boughtAmount;
 | 
			
		||||
 
 | 
			
		||||
@@ -57,13 +57,26 @@ contract MixinCurve {
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeCurveInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeCurveInternal(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        // Decode the bridge data to get the Curve metadata.
 | 
			
		||||
        CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
 | 
			
		||||
        uint256 payableAmount;
 | 
			
		||||
        if (sellToken == WETH) {
 | 
			
		||||
        if (sellToken == weth) {
 | 
			
		||||
            payableAmount = sellAmount;
 | 
			
		||||
            WETH.withdraw(sellAmount);
 | 
			
		||||
            weth.withdraw(sellAmount);
 | 
			
		||||
        } else {
 | 
			
		||||
            sellToken.approveIfBelow(data.curveAddress, sellAmount);
 | 
			
		||||
        }
 | 
			
		||||
@@ -83,9 +96,9 @@ contract MixinCurve {
 | 
			
		||||
            resultData.rrevert();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (buyToken == WETH) {
 | 
			
		||||
        if (buyToken == weth) {
 | 
			
		||||
            boughtAmount = address(this).balance;
 | 
			
		||||
            WETH.deposit{ value: boughtAmount }();
 | 
			
		||||
            weth.deposit{ value: boughtAmount }();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@ interface IKyberNetworkProxy {
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        returns (uint256 boughtAmount);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract MixinKyber {
 | 
			
		||||
@@ -78,12 +79,26 @@ contract MixinKyber {
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeKyberInternal(KYBER_ETH_ADDRESS, WETH, sellToken, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeKyberInternal(
 | 
			
		||||
        IERC20TokenV06 kyberEthAddress,
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (IKyberNetworkProxy kyber, bytes memory hint) =
 | 
			
		||||
            abi.decode(bridgeData, (IKyberNetworkProxy, bytes));
 | 
			
		||||
 | 
			
		||||
        uint256 payableAmount = 0;
 | 
			
		||||
        if (sellToken != WETH) {
 | 
			
		||||
        if (sellToken != weth) {
 | 
			
		||||
            // If the input token is not WETH, grant an allowance to the exchange
 | 
			
		||||
            // to spend them.
 | 
			
		||||
            sellToken.approveIfBelow(
 | 
			
		||||
@@ -93,18 +108,18 @@ contract MixinKyber {
 | 
			
		||||
        } else {
 | 
			
		||||
            // If the input token is WETH, unwrap it and attach it to the call.
 | 
			
		||||
            payableAmount = sellAmount;
 | 
			
		||||
            WETH.withdraw(payableAmount);
 | 
			
		||||
            weth.withdraw(payableAmount);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Try to sell all of this contract's input token balance through
 | 
			
		||||
        // `KyberNetworkProxy.trade()`.
 | 
			
		||||
        boughtAmount = kyber.tradeWithHint{ value: payableAmount }(
 | 
			
		||||
            // Input token.
 | 
			
		||||
            sellToken == WETH ? KYBER_ETH_ADDRESS : sellToken,
 | 
			
		||||
            sellToken == weth ? kyberEthAddress : sellToken,
 | 
			
		||||
            // Sell amount.
 | 
			
		||||
            sellAmount,
 | 
			
		||||
            // Output token.
 | 
			
		||||
            buyToken == WETH ? KYBER_ETH_ADDRESS : buyToken,
 | 
			
		||||
            buyToken == weth ? kyberEthAddress : buyToken,
 | 
			
		||||
            // Transfer to this contract
 | 
			
		||||
            address(uint160(address(this))),
 | 
			
		||||
            // Buy as much as possible.
 | 
			
		||||
@@ -116,8 +131,8 @@ contract MixinKyber {
 | 
			
		||||
            hint
 | 
			
		||||
        );
 | 
			
		||||
        // If receving ETH, wrap it to WETH.
 | 
			
		||||
        if (buyToken == WETH) {
 | 
			
		||||
            WETH.deposit{ value: boughtAmount }();
 | 
			
		||||
        if (buyToken == weth) {
 | 
			
		||||
            weth.deposit{ value: boughtAmount }();
 | 
			
		||||
        }
 | 
			
		||||
        return boughtAmount;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,8 @@ import "../IBridgeAdapter.sol";
 | 
			
		||||
*/
 | 
			
		||||
interface IKyberDmmRouter {
 | 
			
		||||
 | 
			
		||||
    function factory() external view returns (address);
 | 
			
		||||
 | 
			
		||||
    /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path.
 | 
			
		||||
    ///      The first element of path is the input token, the last is the output token, and any intermediate elements represent
 | 
			
		||||
    ///      intermediate pairs to trade through (if, for example, a direct pair does not exist).
 | 
			
		||||
 
 | 
			
		||||
@@ -58,10 +58,23 @@ contract MixinLido {
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeLidoInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeLidoInternal(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (ILido lido) = abi.decode(bridgeData, (ILido));
 | 
			
		||||
        if (address(sellToken) == address(WETH) && address(buyToken) == address(lido)) {
 | 
			
		||||
            WETH.withdraw(sellAmount);
 | 
			
		||||
        if (address(sellToken) == address(weth) && address(buyToken) == address(lido)) {
 | 
			
		||||
            weth.withdraw(sellAmount);
 | 
			
		||||
            boughtAmount = lido.getPooledEthByShares(lido.submit{ value: sellAmount}(address(0)));
 | 
			
		||||
        } else {
 | 
			
		||||
            revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
 | 
			
		||||
 
 | 
			
		||||
@@ -66,12 +66,26 @@ contract MixinMooniswap {
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        return _tradeMooniswapInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeMooniswapInternal(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (IMooniswapPool pool) = abi.decode(bridgeData, (IMooniswapPool));
 | 
			
		||||
 | 
			
		||||
        // Convert WETH to ETH.
 | 
			
		||||
        uint256 ethValue = 0;
 | 
			
		||||
        if (sellToken == WETH) {
 | 
			
		||||
            WETH.withdraw(sellAmount);
 | 
			
		||||
        if (sellToken == weth) {
 | 
			
		||||
            weth.withdraw(sellAmount);
 | 
			
		||||
            ethValue = sellAmount;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Grant the pool an allowance.
 | 
			
		||||
@@ -82,16 +96,16 @@ contract MixinMooniswap {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        boughtAmount = pool.swap{value: ethValue}(
 | 
			
		||||
            sellToken == WETH ? IERC20TokenV06(0) : sellToken,
 | 
			
		||||
            buyToken == WETH ? IERC20TokenV06(0) : buyToken,
 | 
			
		||||
            sellToken == weth ? IERC20TokenV06(0) : sellToken,
 | 
			
		||||
            buyToken == weth ? IERC20TokenV06(0) : buyToken,
 | 
			
		||||
            sellAmount,
 | 
			
		||||
            1,
 | 
			
		||||
            address(0)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Wrap ETH to WETH.
 | 
			
		||||
        if (buyToken == WETH) {
 | 
			
		||||
            WETH.deposit{value:boughtAmount}();
 | 
			
		||||
        if (buyToken == weth) {
 | 
			
		||||
            weth.deposit{value:boughtAmount}();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -124,21 +124,35 @@ contract MixinUniswap {
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeUniswapInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _tradeUniswapInternal(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        bytes memory bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        IUniswapExchangeFactory exchangeFactory =
 | 
			
		||||
            abi.decode(bridgeData, (IUniswapExchangeFactory));
 | 
			
		||||
 | 
			
		||||
        // Get the exchange for the token pair.
 | 
			
		||||
        IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
 | 
			
		||||
            weth,
 | 
			
		||||
            exchangeFactory,
 | 
			
		||||
            sellToken,
 | 
			
		||||
            buyToken
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Convert from WETH to a token.
 | 
			
		||||
        if (sellToken == WETH) {
 | 
			
		||||
        if (sellToken == weth) {
 | 
			
		||||
            // Unwrap the WETH.
 | 
			
		||||
            WETH.withdraw(sellAmount);
 | 
			
		||||
            weth.withdraw(sellAmount);
 | 
			
		||||
            // Buy as much of `buyToken` token with ETH as possible
 | 
			
		||||
            boughtAmount = exchange.ethToTokenTransferInput{ value: sellAmount }(
 | 
			
		||||
                // Minimum buy amount.
 | 
			
		||||
@@ -150,7 +164,7 @@ contract MixinUniswap {
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        // Convert from a token to WETH.
 | 
			
		||||
        } else if (buyToken == WETH) {
 | 
			
		||||
        } else if (buyToken == weth) {
 | 
			
		||||
            // Grant the exchange an allowance.
 | 
			
		||||
            sellToken.approveIfBelow(
 | 
			
		||||
                address(exchange),
 | 
			
		||||
@@ -166,7 +180,7 @@ contract MixinUniswap {
 | 
			
		||||
                block.timestamp
 | 
			
		||||
            );
 | 
			
		||||
            // Wrap the ETH.
 | 
			
		||||
            WETH.deposit{ value: boughtAmount }();
 | 
			
		||||
            weth.deposit{ value: boughtAmount }();
 | 
			
		||||
        // Convert from one token to another.
 | 
			
		||||
        } else {
 | 
			
		||||
            // Grant the exchange an allowance.
 | 
			
		||||
@@ -200,6 +214,7 @@ contract MixinUniswap {
 | 
			
		||||
    /// @param buyToken The address of the token we are converting to.
 | 
			
		||||
    /// @return exchange The uniswap exchange.
 | 
			
		||||
    function _getUniswapExchangeForTokenPair(
 | 
			
		||||
        IEtherTokenV06 weth,
 | 
			
		||||
        IUniswapExchangeFactory exchangeFactory,
 | 
			
		||||
        IERC20TokenV06 sellToken,
 | 
			
		||||
        IERC20TokenV06 buyToken
 | 
			
		||||
@@ -209,7 +224,7 @@ contract MixinUniswap {
 | 
			
		||||
        returns (IUniswapExchange exchange)
 | 
			
		||||
    {
 | 
			
		||||
        // Whichever isn't WETH is the exchange token.
 | 
			
		||||
        exchange = sellToken == WETH
 | 
			
		||||
        exchange = sellToken == weth
 | 
			
		||||
            ? exchangeFactory.getExchange(buyToken)
 | 
			
		||||
            : exchangeFactory.getExchange(sellToken);
 | 
			
		||||
        require(address(exchange) != address(0), "MixinUniswap/NO_EXCHANGE");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,32 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "7.0.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "SwapRevertSampler. Refactored sampler to use exchange functions. Remove gas schedule.",
 | 
			
		||||
                "pr": 245
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "timestamp": 1624562704,
 | 
			
		||||
        "version": "6.18.2",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Dependencies updated"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "6.18.1",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "FirebirdOneSwap, ApeSwap. New hop tokens: DFYN, BANANA, WEXPOLY",
 | 
			
		||||
                "pr": 265
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        "timestamp": 1624405040
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "6.18.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
 | 
			
		||||
 | 
			
		||||
CHANGELOG
 | 
			
		||||
 | 
			
		||||
## v6.18.2 - _June 24, 2021_
 | 
			
		||||
 | 
			
		||||
    * Dependencies updated
 | 
			
		||||
 | 
			
		||||
## v6.18.1 - _June 22, 2021_
 | 
			
		||||
 | 
			
		||||
    * FirebirdOneSwap, ApeSwap. New hop tokens: DFYN, BANANA, WEXPOLY (#265)
 | 
			
		||||
 | 
			
		||||
## v6.18.0 - _June 22, 2021_
 | 
			
		||||
 | 
			
		||||
    * Add Lido stETH deposit integration (#260)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,144 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract ApproximateBuys {
 | 
			
		||||
 | 
			
		||||
    /// @dev Information computing buy quotes for sources that do not have native
 | 
			
		||||
    ///      buy quote support.
 | 
			
		||||
    struct ApproximateBuyQuoteOpts {
 | 
			
		||||
        // Arbitrary maker token data to pass to `getSellQuoteCallback`.
 | 
			
		||||
        bytes makerTokenData;
 | 
			
		||||
        // Arbitrary taker token data to pass to `getSellQuoteCallback`.
 | 
			
		||||
        bytes takerTokenData;
 | 
			
		||||
        // Callback to retrieve a sell quote.
 | 
			
		||||
        function (bytes memory, bytes memory, uint256)
 | 
			
		||||
            internal
 | 
			
		||||
            view
 | 
			
		||||
            returns (uint256) getSellQuoteCallback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
 | 
			
		||||
    /// @dev Maximum approximate (positive) error rate when approximating a buy quote.
 | 
			
		||||
    uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
 | 
			
		||||
    /// @dev Maximum iterations to perform when approximating a buy quote.
 | 
			
		||||
    uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 5;
 | 
			
		||||
 | 
			
		||||
    function _sampleApproximateBuys(
 | 
			
		||||
        ApproximateBuyQuoteOpts memory opts,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        takerTokenAmounts = new uint256[](makerTokenAmounts.length);
 | 
			
		||||
        if (makerTokenAmounts.length == 0) {
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint256 sellAmount = opts.getSellQuoteCallback(
 | 
			
		||||
            opts.makerTokenData,
 | 
			
		||||
            opts.takerTokenData,
 | 
			
		||||
            makerTokenAmounts[0]
 | 
			
		||||
        );
 | 
			
		||||
        if (sellAmount == 0) {
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint256 buyAmount = opts.getSellQuoteCallback(
 | 
			
		||||
            opts.takerTokenData,
 | 
			
		||||
            opts.makerTokenData,
 | 
			
		||||
            sellAmount
 | 
			
		||||
        );
 | 
			
		||||
        if (buyAmount == 0) {
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
 | 
			
		||||
            for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
 | 
			
		||||
                // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
 | 
			
		||||
                sellAmount = _safeGetPartialAmountCeil(
 | 
			
		||||
                    makerTokenAmounts[i],
 | 
			
		||||
                    buyAmount,
 | 
			
		||||
                    sellAmount
 | 
			
		||||
                );
 | 
			
		||||
                if (sellAmount == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                sellAmount = _safeGetPartialAmountCeil(
 | 
			
		||||
                    (ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
 | 
			
		||||
                    ONE_HUNDED_PERCENT_BPS,
 | 
			
		||||
                    sellAmount
 | 
			
		||||
                );
 | 
			
		||||
                if (sellAmount == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                uint256 _buyAmount = opts.getSellQuoteCallback(
 | 
			
		||||
                    opts.takerTokenData,
 | 
			
		||||
                    opts.makerTokenData,
 | 
			
		||||
                    sellAmount
 | 
			
		||||
                );
 | 
			
		||||
                if (_buyAmount == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // We re-use buyAmount next iteration, only assign if it is
 | 
			
		||||
                // non zero
 | 
			
		||||
                buyAmount = _buyAmount;
 | 
			
		||||
                // If we've reached our goal, exit early
 | 
			
		||||
                if (buyAmount >= makerTokenAmounts[i]) {
 | 
			
		||||
                    uint256 eps =
 | 
			
		||||
                        (buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
 | 
			
		||||
                        makerTokenAmounts[i];
 | 
			
		||||
                    if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // We do our best to close in on the requested amount, but we can either over buy or under buy and exit
 | 
			
		||||
            // if we hit a max iteration limit
 | 
			
		||||
            // We scale the sell amount to get the approximate target
 | 
			
		||||
            takerTokenAmounts[i] = _safeGetPartialAmountCeil(
 | 
			
		||||
                makerTokenAmounts[i],
 | 
			
		||||
                buyAmount,
 | 
			
		||||
                sellAmount
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _safeGetPartialAmountCeil(
 | 
			
		||||
        uint256 numerator,
 | 
			
		||||
        uint256 denominator,
 | 
			
		||||
        uint256 target
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 partialAmount)
 | 
			
		||||
    {
 | 
			
		||||
        if (numerator == 0 || target == 0 || denominator == 0) return 0;
 | 
			
		||||
        uint256 c = numerator * target;
 | 
			
		||||
        if (c / numerator != target) return 0;
 | 
			
		||||
        return (c + (denominator - 1)) / denominator;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,26 +20,29 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IBalancer.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBalancer.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract BalancerSampler is
 | 
			
		||||
    MixinBalancer,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
contract BalancerSampler {
 | 
			
		||||
 | 
			
		||||
    /// @dev Base gas limit for Balancer calls.
 | 
			
		||||
    uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k
 | 
			
		||||
 | 
			
		||||
    // Balancer math constants
 | 
			
		||||
    // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
 | 
			
		||||
    uint256 constant private BONE = 10 ** 18;
 | 
			
		||||
    uint256 constant private MAX_IN_RATIO = BONE / 2;
 | 
			
		||||
    uint256 constant private MAX_OUT_RATIO = (BONE / 3) + 1 wei;
 | 
			
		||||
 | 
			
		||||
    struct BalancerState {
 | 
			
		||||
        uint256 takerTokenBalance;
 | 
			
		||||
        uint256 makerTokenBalance;
 | 
			
		||||
        uint256 takerTokenWeight;
 | 
			
		||||
        uint256 makerTokenWeight;
 | 
			
		||||
        uint256 swapFee;
 | 
			
		||||
    function sampleSwapFromBalancer(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeBalancer(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Balancer.
 | 
			
		||||
@@ -47,6 +50,7 @@ contract BalancerSampler {
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromBalancer(
 | 
			
		||||
@@ -56,52 +60,17 @@ contract BalancerSampler {
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        IBalancer pool = IBalancer(poolAddress);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        BalancerState memory poolState;
 | 
			
		||||
        poolState.takerTokenBalance = pool.getBalance(takerToken);
 | 
			
		||||
        poolState.makerTokenBalance = pool.getBalance(makerToken);
 | 
			
		||||
        poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
 | 
			
		||||
        poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
 | 
			
		||||
        poolState.swapFee = pool.getSwapFee();
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            // Handles this revert scenario:
 | 
			
		||||
            // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443
 | 
			
		||||
            if (takerTokenAmounts[i] > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            try
 | 
			
		||||
                pool.calcOutGivenIn
 | 
			
		||||
                    {gas: BALANCER_CALL_GAS}
 | 
			
		||||
                    (
 | 
			
		||||
                        poolState.takerTokenBalance,
 | 
			
		||||
                        poolState.takerTokenWeight,
 | 
			
		||||
                        poolState.makerTokenBalance,
 | 
			
		||||
                        poolState.makerTokenWeight,
 | 
			
		||||
                        takerTokenAmounts[i],
 | 
			
		||||
                        poolState.swapFee
 | 
			
		||||
                    )
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(poolAddress),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromBalancer
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Balancer.
 | 
			
		||||
@@ -109,6 +78,7 @@ contract BalancerSampler {
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromBalancer(
 | 
			
		||||
@@ -118,74 +88,18 @@ contract BalancerSampler {
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        IBalancer pool = IBalancer(poolAddress);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(poolAddress),
 | 
			
		||||
                buyTokenData: abi.encode(poolAddress),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromBalancer
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        BalancerState memory poolState;
 | 
			
		||||
        poolState.takerTokenBalance = pool.getBalance(takerToken);
 | 
			
		||||
        poolState.makerTokenBalance = pool.getBalance(makerToken);
 | 
			
		||||
        poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
 | 
			
		||||
        poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
 | 
			
		||||
        poolState.swapFee = pool.getSwapFee();
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            // Handles this revert scenario:
 | 
			
		||||
            // https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L505
 | 
			
		||||
            if (makerTokenAmounts[i] > _bmul(poolState.makerTokenBalance, MAX_OUT_RATIO)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            try
 | 
			
		||||
                pool.calcInGivenOut
 | 
			
		||||
                    {gas: BALANCER_CALL_GAS}
 | 
			
		||||
                    (
 | 
			
		||||
                        poolState.takerTokenBalance,
 | 
			
		||||
                        poolState.takerTokenWeight,
 | 
			
		||||
                        poolState.makerTokenBalance,
 | 
			
		||||
                        poolState.makerTokenWeight,
 | 
			
		||||
                        makerTokenAmounts[i],
 | 
			
		||||
                        poolState.swapFee
 | 
			
		||||
                    )
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                takerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (takerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Hacked version of Balancer's `bmul` function, returning 0 instead
 | 
			
		||||
    ///      of reverting.
 | 
			
		||||
    ///      https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L63-L73
 | 
			
		||||
    /// @param a The first operand.
 | 
			
		||||
    /// @param b The second operand.
 | 
			
		||||
    /// @param c The result of the multiplication, or 0 if `bmul` would've reverted.
 | 
			
		||||
    function _bmul(uint256 a, uint256 b)
 | 
			
		||||
        private
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256 c)
 | 
			
		||||
    {
 | 
			
		||||
        uint c0 = a * b;
 | 
			
		||||
        if (a != 0 && c0 / a != b) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        uint c1 = c0 + (BONE / 2);
 | 
			
		||||
        if (c1 < c0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        uint c2 = c1 / BONE;
 | 
			
		||||
        return c2;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,44 +20,29 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBalancerV2.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
/// @dev Minimal Balancer V2 Vault interface
 | 
			
		||||
///      for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
 | 
			
		||||
interface IBalancerV2Vault {
 | 
			
		||||
    enum SwapKind { GIVEN_IN, GIVEN_OUT }
 | 
			
		||||
contract BalancerV2Sampler is
 | 
			
		||||
    MixinBalancerV2,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    struct BatchSwapStep {
 | 
			
		||||
        bytes32 poolId;
 | 
			
		||||
        uint256 assetInIndex;
 | 
			
		||||
        uint256 assetOutIndex;
 | 
			
		||||
        uint256 amount;
 | 
			
		||||
        bytes userData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct FundManagement {
 | 
			
		||||
        address sender;
 | 
			
		||||
        bool fromInternalBalance;
 | 
			
		||||
        address payable recipient;
 | 
			
		||||
        bool toInternalBalance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function queryBatchSwap(
 | 
			
		||||
        SwapKind kind,
 | 
			
		||||
        BatchSwapStep[] calldata swaps,
 | 
			
		||||
        IAsset[] calldata assets,
 | 
			
		||||
        FundManagement calldata funds
 | 
			
		||||
    ) external returns (int256[] memory assetDeltas);
 | 
			
		||||
}
 | 
			
		||||
interface IAsset {
 | 
			
		||||
    // solhint-disable-previous-line no-empty-blocks
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract BalancerV2Sampler is SamplerUtils {
 | 
			
		||||
 | 
			
		||||
    struct BalancerV2PoolInfo {
 | 
			
		||||
        bytes32 poolId;
 | 
			
		||||
        address vault;
 | 
			
		||||
    function sampleSwapFromBalancerV2(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeBalancerV2(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Balancer V2.
 | 
			
		||||
@@ -65,48 +50,27 @@ contract BalancerV2Sampler is SamplerUtils {
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromBalancerV2(
 | 
			
		||||
        BalancerV2PoolInfo memory poolInfo,
 | 
			
		||||
        BalancerV2BridgeData memory poolInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
 | 
			
		||||
        IAsset[] memory swapAssets = new IAsset[](2);
 | 
			
		||||
        swapAssets[0] = IAsset(takerToken);
 | 
			
		||||
        swapAssets[1] = IAsset(makerToken);
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        IBalancerV2Vault.FundManagement memory swapFunds =
 | 
			
		||||
            _createSwapFunds();
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
 | 
			
		||||
                _createSwapSteps(poolInfo, takerTokenAmounts[i]);
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
                // For sells we specify the takerToken which is what the vault will receive from the trade
 | 
			
		||||
                vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds)
 | 
			
		||||
            // amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
 | 
			
		||||
            returns (int256[] memory amounts) {
 | 
			
		||||
                // Outgoing balance is negative so we need to flip the sign
 | 
			
		||||
                int256 amountOutFromPool = amounts[1] * -1;
 | 
			
		||||
                if (amountOutFromPool <= 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                makerTokenAmounts[i] = uint256(amountOutFromPool);
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(poolInfo),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromBalancerV2
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Balancer V2.
 | 
			
		||||
@@ -114,76 +78,27 @@ contract BalancerV2Sampler is SamplerUtils {
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromBalancerV2(
 | 
			
		||||
        BalancerV2PoolInfo memory poolInfo,
 | 
			
		||||
        BalancerV2BridgeData memory poolInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
 | 
			
		||||
        IAsset[] memory swapAssets = new IAsset[](2);
 | 
			
		||||
        swapAssets[0] = IAsset(takerToken);
 | 
			
		||||
        swapAssets[1] = IAsset(makerToken);
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        IBalancerV2Vault.FundManagement memory swapFunds =
 | 
			
		||||
            _createSwapFunds();
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
 | 
			
		||||
                _createSwapSteps(poolInfo, makerTokenAmounts[i]);
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
                // For buys we specify the makerToken which is what taker will receive from the trade
 | 
			
		||||
                vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds)
 | 
			
		||||
            returns (int256[] memory amounts) {
 | 
			
		||||
                int256 amountIntoPool = amounts[0];
 | 
			
		||||
                if (amountIntoPool <= 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                takerTokenAmounts[i] = uint256(amountIntoPool);
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _createSwapSteps(
 | 
			
		||||
        BalancerV2PoolInfo memory poolInfo,
 | 
			
		||||
        uint256 amount
 | 
			
		||||
    ) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
 | 
			
		||||
        IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
 | 
			
		||||
            new IBalancerV2Vault.BatchSwapStep[](1);
 | 
			
		||||
        swapSteps[0] = IBalancerV2Vault.BatchSwapStep({
 | 
			
		||||
            poolId: poolInfo.poolId,
 | 
			
		||||
            assetInIndex: 0,
 | 
			
		||||
            assetOutIndex: 1,
 | 
			
		||||
            amount: amount,
 | 
			
		||||
            userData: ""
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return swapSteps;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _createSwapFunds()
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (IBalancerV2Vault.FundManagement memory)
 | 
			
		||||
    {
 | 
			
		||||
        return
 | 
			
		||||
            IBalancerV2Vault.FundManagement({
 | 
			
		||||
                sender: address(this),
 | 
			
		||||
                fromInternalBalance: false,
 | 
			
		||||
                recipient: payable(address(this)),
 | 
			
		||||
                toInternalBalance: false
 | 
			
		||||
            });
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(poolInfo),
 | 
			
		||||
                buyTokenData: abi.encode(poolInfo),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromBalancerV2
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,11 +20,21 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IBancor.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBancor.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract CompilerHack {}
 | 
			
		||||
 | 
			
		||||
contract BancorSampler is CompilerHack {
 | 
			
		||||
contract BancorSampler is
 | 
			
		||||
    CompilerHack,
 | 
			
		||||
    MixinBancor,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinBancor(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// @dev Base gas limit for Bancor calls.
 | 
			
		||||
    uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
 | 
			
		||||
@@ -34,6 +44,23 @@ contract BancorSampler is CompilerHack {
 | 
			
		||||
        address[][] paths;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromBancor(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeBancorInternal(
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Bancor.
 | 
			
		||||
    /// @param opts BancorSamplerOpts The Bancor registry contract address and paths
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
@@ -41,6 +68,7 @@ contract BancorSampler is CompilerHack {
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return bancorNetwork the Bancor Network address
 | 
			
		||||
    /// @return path the selected conversion path from bancor
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromBancor(
 | 
			
		||||
@@ -50,34 +78,30 @@ contract BancorSampler is CompilerHack {
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            address bancorNetwork,
 | 
			
		||||
            address[] memory path,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        if (opts.paths.length == 0) {
 | 
			
		||||
            return (bancorNetwork, path, makerTokenAmounts);
 | 
			
		||||
            return (bancorNetwork, path, gasUsed, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        (bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts);
 | 
			
		||||
        makerTokenAmounts = new uint256[](takerTokenAmounts.length);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IBancorNetwork(bancorNetwork)
 | 
			
		||||
                    .rateByPath
 | 
			
		||||
                        {gas: BANCOR_CALL_GAS}
 | 
			
		||||
                        (path, takerTokenAmounts[i])
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return (bancorNetwork, path, makerTokenAmounts);
 | 
			
		||||
        (bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(bancorNetwork, path),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromBancor
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return (bancorNetwork, path, gasUsed, makerTokenAmounts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Bancor. Unimplemented
 | 
			
		||||
@@ -87,6 +111,7 @@ contract BancorSampler is CompilerHack {
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return bancorNetwork the Bancor Network address
 | 
			
		||||
    /// @return path the selected conversion path from bancor
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromBancor(
 | 
			
		||||
@@ -97,7 +122,7 @@ contract BancorSampler is CompilerHack {
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (address bancorNetwork, address[] memory path, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (address bancorNetwork, address[] memory path, uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,142 +20,99 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/ICurve.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinCurve.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract CurveSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinCurve,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Information for sampling from curve sources.
 | 
			
		||||
    struct CurveInfo {
 | 
			
		||||
        address poolAddress;
 | 
			
		||||
        bytes4 sellQuoteFunctionSelector;
 | 
			
		||||
        bytes4 buyQuoteFunctionSelector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
 | 
			
		||||
    ///      So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
 | 
			
		||||
    uint256 constant private CURVE_CALL_GAS = 2000e3; // Was 600k for Curve but SnowSwap is using 1500k+
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinCurve(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromCurve(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeCurveInternal(
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Curve.
 | 
			
		||||
    /// @param curveInfo Curve information specific to this token pair.
 | 
			
		||||
    /// @param fromTokenIdx Index of the taker token (what to sell).
 | 
			
		||||
    /// @param toTokenIdx Index of the maker token (what to buy).
 | 
			
		||||
    /// @param takerToken The taker token to sell.
 | 
			
		||||
    /// @param makerToken The maker token to buy.
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromCurve(
 | 
			
		||||
        CurveInfo memory curveInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        CurveBridgeData memory curveInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        curveInfo.sellQuoteFunctionSelector,
 | 
			
		||||
                        fromTokenIdx,
 | 
			
		||||
                        toTokenIdx,
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(curveInfo),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromCurve
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Curve.
 | 
			
		||||
    /// @param curveInfo Curve information specific to this token pair.
 | 
			
		||||
    /// @param fromTokenIdx Index of the taker token (what to sell).
 | 
			
		||||
    /// @param toTokenIdx Index of the maker token (what to buy).
 | 
			
		||||
    /// @param takerToken The taker token to sell.
 | 
			
		||||
    /// @param makerToken The maker token to buy.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromCurve(
 | 
			
		||||
        CurveInfo memory curveInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        CurveBridgeData memory curveInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        if (curveInfo.buyQuoteFunctionSelector == bytes4(0)) {
 | 
			
		||||
            // Buys not supported on this curve, so approximate it.
 | 
			
		||||
            return _sampleApproximateBuys(
 | 
			
		||||
                ApproximateBuyQuoteOpts({
 | 
			
		||||
                    makerTokenData: abi.encode(toTokenIdx, curveInfo),
 | 
			
		||||
                    takerTokenData: abi.encode(fromTokenIdx, curveInfo),
 | 
			
		||||
                    getSellQuoteCallback: _sampleSellForApproximateBuyFromCurve
 | 
			
		||||
                }),
 | 
			
		||||
                makerTokenAmounts
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        curveInfo.buyQuoteFunctionSelector,
 | 
			
		||||
                        fromTokenIdx,
 | 
			
		||||
                        toTokenIdx,
 | 
			
		||||
                        makerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 sellAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                sellAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            }
 | 
			
		||||
            takerTokenAmounts[i] = sellAmount;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (takerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromCurve(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (int128 takerTokenIdx, CurveInfo memory curveInfo) =
 | 
			
		||||
            abi.decode(takerTokenData, (int128, CurveInfo));
 | 
			
		||||
        (int128 makerTokenIdx) =
 | 
			
		||||
            abi.decode(makerTokenData, (int128));
 | 
			
		||||
        (bool success, bytes memory resultData) =
 | 
			
		||||
            address(this).staticcall(abi.encodeWithSelector(
 | 
			
		||||
                this.sampleSellsFromCurve.selector,
 | 
			
		||||
                curveInfo,
 | 
			
		||||
                takerTokenIdx,
 | 
			
		||||
                makerTokenIdx,
 | 
			
		||||
                _toSingleValueArray(sellAmount)
 | 
			
		||||
            ));
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // solhint-disable-next-line indent
 | 
			
		||||
        return abi.decode(resultData, (uint256[]))[0];
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(curveInfo),
 | 
			
		||||
                buyTokenData: abi.encode(
 | 
			
		||||
                    CurveBridgeData({
 | 
			
		||||
                        curveAddress: curveInfo.curveAddress,
 | 
			
		||||
                        exchangeFunctionSelector: curveInfo.exchangeFunctionSelector,
 | 
			
		||||
                        fromCoinIdx: curveInfo.toCoinIdx,
 | 
			
		||||
                        toCoinIdx: curveInfo.fromCoinIdx
 | 
			
		||||
                    })
 | 
			
		||||
                ),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromCurve
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								packages/asset-swapper/contracts/src/CurveV2Sampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								packages/asset-swapper/contracts/src/CurveV2Sampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2021 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinCurveV2.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract CurveV2Sampler is
 | 
			
		||||
    MixinCurveV2,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromCurveV2(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeCurveV2(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Curve.
 | 
			
		||||
    /// @param curveInfo Curve information specific to this token pair.
 | 
			
		||||
    /// @param takerToken The taker token to sell.
 | 
			
		||||
    /// @param makerToken The maker token to buy.
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromCurveV2(
 | 
			
		||||
        CurveBridgeDataV2 memory curveInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(curveInfo),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromCurveV2
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Curve.
 | 
			
		||||
    /// @param curveInfo Curve information specific to this token pair.
 | 
			
		||||
    /// @param takerToken The taker token to sell.
 | 
			
		||||
    /// @param makerToken The maker token to buy.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromCurveV2(
 | 
			
		||||
        CurveBridgeDataV2 memory curveInfo,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(curveInfo),
 | 
			
		||||
                buyTokenData: abi.encode(
 | 
			
		||||
                    CurveBridgeDataV2({
 | 
			
		||||
                        curveAddress: curveInfo.curveAddress,
 | 
			
		||||
                        exchangeFunctionSelector: curveInfo.exchangeFunctionSelector,
 | 
			
		||||
                        fromCoinIdx: curveInfo.toCoinIdx,
 | 
			
		||||
                        toCoinIdx: curveInfo.fromCoinIdx
 | 
			
		||||
                    })
 | 
			
		||||
                ),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromCurveV2
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,35 +20,40 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinDodo.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IDODOZoo {
 | 
			
		||||
    function getDODO(address baseToken, address quoteToken) external view returns (address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IDODOHelper {
 | 
			
		||||
    function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IDODO {
 | 
			
		||||
    function querySellBaseToken(uint256 amount) external view returns (uint256);
 | 
			
		||||
    function _TRADE_ALLOWED_() external view returns (bool);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract DODOSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinDodo,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// @dev Gas limit for DODO calls.
 | 
			
		||||
    uint256 constant private DODO_CALL_GAS = 300e3; // 300k
 | 
			
		||||
    struct DODOSamplerOpts {
 | 
			
		||||
        address registry;
 | 
			
		||||
        address helper;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromDodo(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeDodo(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from DODO.
 | 
			
		||||
    /// @param opts DODOSamplerOpts DODO Registry and helper addresses
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
@@ -56,6 +61,7 @@ contract DODOSampler is
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return sellBase whether the bridge needs to sell the base token
 | 
			
		||||
    /// @return pool the DODO pool address
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromDODO(
 | 
			
		||||
@@ -65,13 +71,13 @@ contract DODOSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bool sellBase,
 | 
			
		||||
            address pool,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
 | 
			
		||||
        address baseToken;
 | 
			
		||||
        // If pool exists we have the correct order of Base/Quote
 | 
			
		||||
@@ -82,29 +88,21 @@ contract DODOSampler is
 | 
			
		||||
            pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
 | 
			
		||||
            // No pool either direction
 | 
			
		||||
            if (address(pool) == address(0)) {
 | 
			
		||||
                return (sellBase, pool, makerTokenAmounts);
 | 
			
		||||
                return (sellBase, pool, gasUsed, makerTokenAmounts);
 | 
			
		||||
            }
 | 
			
		||||
            baseToken = makerToken;
 | 
			
		||||
            sellBase = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // DODO Pool has been disabled
 | 
			
		||||
        if (!IDODO(pool)._TRADE_ALLOWED_()) {
 | 
			
		||||
            return (sellBase, pool, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 buyAmount = _sampleSellForApproximateBuyFromDODO(
 | 
			
		||||
                abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data
 | 
			
		||||
                abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data
 | 
			
		||||
                takerTokenAmounts[i]
 | 
			
		||||
            );
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(opts.helper, pool, sellBase),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromDodo
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from DODO.
 | 
			
		||||
@@ -114,6 +112,7 @@ contract DODOSampler is
 | 
			
		||||
    /// @param makerTokenAmounts Maker token sell amount for each sample.
 | 
			
		||||
    /// @return sellBase whether the bridge needs to sell the base token
 | 
			
		||||
    /// @return pool the DODO pool address
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromDODO(
 | 
			
		||||
@@ -123,13 +122,13 @@ contract DODOSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bool sellBase,
 | 
			
		||||
            address pool,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory takerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        // Pool is BASE/QUOTE
 | 
			
		||||
        // Look up the pool from the taker/maker combination
 | 
			
		||||
        pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
 | 
			
		||||
@@ -143,69 +142,21 @@ contract DODOSampler is
 | 
			
		||||
            pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
 | 
			
		||||
            // No pool either direction
 | 
			
		||||
            if (address(pool) == address(0)) {
 | 
			
		||||
                return (sellBase, pool, takerTokenAmounts);
 | 
			
		||||
                return (sellBase, pool, gasUsed, takerTokenAmounts);
 | 
			
		||||
            }
 | 
			
		||||
            baseToken = makerToken;
 | 
			
		||||
            sellBase = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // DODO Pool has been disabled
 | 
			
		||||
        if (!IDODO(pool)._TRADE_ALLOWED_()) {
 | 
			
		||||
            return (sellBase, pool, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(opts.helper, pool, sellBase),
 | 
			
		||||
                buyTokenData: abi.encode(opts.helper, pool, !sellBase),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromDodo
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromDODO(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory /* makerTokenData */,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address pool, address baseToken, address helper) = abi.decode(
 | 
			
		||||
            takerTokenData,
 | 
			
		||||
            (address, address, address, address)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // We will get called to sell both the taker token and also to sell the maker token
 | 
			
		||||
        if (takerToken == baseToken) {
 | 
			
		||||
            // If base token then use the original query on the pool
 | 
			
		||||
            try
 | 
			
		||||
                IDODO(pool).querySellBaseToken
 | 
			
		||||
                    {gas: DODO_CALL_GAS}
 | 
			
		||||
                    (sellAmount)
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                return amount;
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // If quote token then use helper, this is less accurate
 | 
			
		||||
            try
 | 
			
		||||
                IDODOHelper(helper).querySellQuoteToken
 | 
			
		||||
                    {gas: DODO_CALL_GAS}
 | 
			
		||||
                    (pool, sellAmount)
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                return amount;
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,8 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinDodoV2.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
interface IDODOV2Registry {
 | 
			
		||||
    function getDODOPool(address baseToken, address quoteToken)
 | 
			
		||||
@@ -30,26 +30,30 @@ interface IDODOV2Registry {
 | 
			
		||||
        returns (address[] memory machines);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IDODOV2Pool {
 | 
			
		||||
    function querySellBase(address trader, uint256 payBaseAmount)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 receiveQuoteAmount, uint256 mtFee);
 | 
			
		||||
 | 
			
		||||
    function querySellQuote(address trader, uint256 payQuoteAmount)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 receiveBaseAmount, uint256 mtFee);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract DODOV2Sampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinDodoV2,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// @dev Gas limit for DODO V2 calls.
 | 
			
		||||
    uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromDodoV2(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeDodoV2(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from DODO V2.
 | 
			
		||||
    /// @param registry Address of the registry to look up.
 | 
			
		||||
    /// @param offset offset index for the pool in the registry.
 | 
			
		||||
@@ -58,6 +62,7 @@ contract DODOV2Sampler is
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return sellBase whether the bridge needs to sell the base token
 | 
			
		||||
    /// @return pool the DODO pool address
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromDODOV2(
 | 
			
		||||
@@ -68,31 +73,27 @@ contract DODOV2Sampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bool sellBase,
 | 
			
		||||
            address pool,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
 | 
			
		||||
        if (pool == address(0)) {
 | 
			
		||||
            return (sellBase, pool, makerTokenAmounts);
 | 
			
		||||
            return (sellBase, pool, gasUsed, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2(
 | 
			
		||||
                abi.encode(takerToken, pool, sellBase), // taker token data
 | 
			
		||||
                abi.encode(makerToken, pool, sellBase), // maker token data
 | 
			
		||||
                takerTokenAmounts[i]
 | 
			
		||||
            );
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(pool, sellBase),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromDodoV2
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from DODO.
 | 
			
		||||
@@ -103,6 +104,7 @@ contract DODOV2Sampler is
 | 
			
		||||
    /// @param makerTokenAmounts Maker token sell amount for each sample.
 | 
			
		||||
    /// @return sellBase whether the bridge needs to sell the base token
 | 
			
		||||
    /// @return pool the DODO pool address
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromDODOV2(
 | 
			
		||||
@@ -113,68 +115,30 @@ contract DODOV2Sampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bool sellBase,
 | 
			
		||||
            address pool,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory takerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
 | 
			
		||||
        if (pool == address(0)) {
 | 
			
		||||
            return (sellBase, pool, takerTokenAmounts);
 | 
			
		||||
            return (sellBase, pool, gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, pool, !sellBase),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, pool, sellBase),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(pool, sellBase),
 | 
			
		||||
                buyTokenData: abi.encode(pool, !sellBase),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromDodoV2
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromDODOV2(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory /* makerTokenData */,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address pool, bool sellBase) = abi.decode(
 | 
			
		||||
            takerTokenData,
 | 
			
		||||
            (address, address, bool)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // We will get called to sell both the taker token and also to sell the maker token
 | 
			
		||||
        // since we use approximate buy for sell and buy functions
 | 
			
		||||
        if (sellBase) {
 | 
			
		||||
            try
 | 
			
		||||
                IDODOV2Pool(pool).querySellBase
 | 
			
		||||
                    { gas: DODO_V2_CALL_GAS }
 | 
			
		||||
                    (address(0), sellAmount)
 | 
			
		||||
                returns (uint256 amount, uint256)
 | 
			
		||||
            {
 | 
			
		||||
                return amount;
 | 
			
		||||
            } catch {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            try
 | 
			
		||||
                IDODOV2Pool(pool).querySellQuote
 | 
			
		||||
                    { gas: DODO_V2_CALL_GAS }
 | 
			
		||||
                    (address(0), sellAmount)
 | 
			
		||||
                returns (uint256 amount, uint256)
 | 
			
		||||
            {
 | 
			
		||||
                return amount;
 | 
			
		||||
            } catch {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getNextDODOV2Pool(
 | 
			
		||||
        address registry,
 | 
			
		||||
        uint256 offset,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								packages/asset-swapper/contracts/src/DelegateHackedERC20.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/asset-swapper/contracts/src/DelegateHackedERC20.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
 | 
			
		||||
contract DelegateHackedERC20 {
 | 
			
		||||
 | 
			
		||||
    address private constant HACKED = 0xDEf1000000000000000000000000000000DE7d37;
 | 
			
		||||
 | 
			
		||||
    receive() external payable {}
 | 
			
		||||
 | 
			
		||||
    fallback() payable external {
 | 
			
		||||
        (bool success, bytes memory resultData) =
 | 
			
		||||
            HACKED.delegatecall(msg.data);
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            assembly { revert(add(resultData, 32), mload(resultData)) }
 | 
			
		||||
        }
 | 
			
		||||
        assembly { return(add(resultData, 32), mload(resultData)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Hack to get around schema validation
 | 
			
		||||
    function _garbage()
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -24,6 +24,7 @@ import "./BalancerSampler.sol";
 | 
			
		||||
import "./BalancerV2Sampler.sol";
 | 
			
		||||
import "./BancorSampler.sol";
 | 
			
		||||
import "./CurveSampler.sol";
 | 
			
		||||
import "./CurveV2Sampler.sol";
 | 
			
		||||
import "./DODOSampler.sol";
 | 
			
		||||
import "./DODOV2Sampler.sol";
 | 
			
		||||
import "./Eth2DaiSampler.sol";
 | 
			
		||||
@@ -32,24 +33,24 @@ import "./KyberDmmSampler.sol";
 | 
			
		||||
import "./LidoSampler.sol";
 | 
			
		||||
import "./LiquidityProviderSampler.sol";
 | 
			
		||||
import "./MakerPSMSampler.sol";
 | 
			
		||||
import "./MultiBridgeSampler.sol";
 | 
			
		||||
import "./MStableSampler.sol";
 | 
			
		||||
import "./MooniswapSampler.sol";
 | 
			
		||||
import "./NativeOrderSampler.sol";
 | 
			
		||||
import "./ShellSampler.sol";
 | 
			
		||||
import "./SmoothySampler.sol";
 | 
			
		||||
import "./TwoHopSampler.sol";
 | 
			
		||||
import "./UniswapSampler.sol";
 | 
			
		||||
import "./UniswapV2Sampler.sol";
 | 
			
		||||
import "./UniswapV3Sampler.sol";
 | 
			
		||||
import "./UtilitySampler.sol";
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
 | 
			
		||||
 | 
			
		||||
contract ERC20BridgeSampler is
 | 
			
		||||
    BalancerSampler,
 | 
			
		||||
    BalancerV2Sampler,
 | 
			
		||||
    BancorSampler,
 | 
			
		||||
    CurveSampler,
 | 
			
		||||
    CurveV2Sampler,
 | 
			
		||||
    DODOSampler,
 | 
			
		||||
    DODOV2Sampler,
 | 
			
		||||
    Eth2DaiSampler,
 | 
			
		||||
@@ -60,10 +61,8 @@ contract ERC20BridgeSampler is
 | 
			
		||||
    MakerPSMSampler,
 | 
			
		||||
    MStableSampler,
 | 
			
		||||
    MooniswapSampler,
 | 
			
		||||
    MultiBridgeSampler,
 | 
			
		||||
    NativeOrderSampler,
 | 
			
		||||
    ShellSampler,
 | 
			
		||||
    SmoothySampler,
 | 
			
		||||
    TwoHopSampler,
 | 
			
		||||
    UniswapSampler,
 | 
			
		||||
    UniswapV2Sampler,
 | 
			
		||||
@@ -76,11 +75,22 @@ contract ERC20BridgeSampler is
 | 
			
		||||
        bool success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        BancorSampler(weth)
 | 
			
		||||
        CurveSampler(weth)
 | 
			
		||||
        KyberSampler(weth)
 | 
			
		||||
        MooniswapSampler(weth)
 | 
			
		||||
        LidoSampler(weth)
 | 
			
		||||
        UniswapSampler(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    /// @dev Call multiple public functions on this contract in a single transaction.
 | 
			
		||||
    /// @param callDatas ABI-encoded call data for each function call.
 | 
			
		||||
    /// @return callResults ABI-encoded results data for each call.
 | 
			
		||||
    function batchCall(bytes[] calldata callDatas)
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        returns (CallResults[] memory callResults)
 | 
			
		||||
    {
 | 
			
		||||
        callResults = new CallResults[](callDatas.length);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,21 +20,38 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IEth2Dai.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinOasis.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract Eth2DaiSampler is
 | 
			
		||||
    SamplerUtils
 | 
			
		||||
    MixinOasis,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Base gas limit for Eth2Dai calls.
 | 
			
		||||
    uint256 constant private ETH2DAI_CALL_GAS = 1000e3; // 1m
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromOasis(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeOasis(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Eth2Dai/Oasis.
 | 
			
		||||
    /// @param router Address of the Eth2Dai/Oasis contract
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromEth2Dai(
 | 
			
		||||
@@ -44,29 +61,17 @@ contract Eth2DaiSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IEth2Dai(router).getBuyAmount
 | 
			
		||||
                    {gas: ETH2DAI_CALL_GAS}
 | 
			
		||||
                    (makerToken, takerToken, takerTokenAmounts[i])
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromOasis
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Eth2Dai/Oasis.
 | 
			
		||||
@@ -74,6 +79,7 @@ contract Eth2DaiSampler is
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Maker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromEth2Dai(
 | 
			
		||||
@@ -83,28 +89,17 @@ contract Eth2DaiSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IEth2Dai(router).getPayAmount
 | 
			
		||||
                    {gas: ETH2DAI_CALL_GAS}
 | 
			
		||||
                    (takerToken, makerToken, makerTokenAmounts[i])
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                takerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (takerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(router),
 | 
			
		||||
                buyTokenData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromOasis
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								packages/asset-swapper/contracts/src/GasOverhead.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								packages/asset-swapper/contracts/src/GasOverhead.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
 | 
			
		||||
contract GasOverhead {
 | 
			
		||||
    uint256 private _overhead = 2;
 | 
			
		||||
    // Overhead incurred from updating the overhead storage slot
 | 
			
		||||
    uint256 constant SSTORE_OVERHEAD = 20000;
 | 
			
		||||
 | 
			
		||||
    function addOverhead(uint256 gas, uint256 gasBefore)
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        uint256 callOverhead = gasBefore - gasleft();
 | 
			
		||||
        // Add additional est overhead of performing this update
 | 
			
		||||
        _overhead += gas + callOverhead + SSTORE_OVERHEAD;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function clearOverhead()
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        _overhead = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function overhead()
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _overhead;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										323
									
								
								packages/asset-swapper/contracts/src/HackedERC20.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								packages/asset-swapper/contracts/src/HackedERC20.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,323 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
 | 
			
		||||
import "./GasOverhead.sol";
 | 
			
		||||
 | 
			
		||||
contract HackedERC20 {
 | 
			
		||||
 | 
			
		||||
    using LibERC20TokenV06 for IERC20TokenV06;
 | 
			
		||||
 | 
			
		||||
    struct ShadowedAmount {
 | 
			
		||||
        bool isShadowed;
 | 
			
		||||
        uint256 lastTrueAmount;
 | 
			
		||||
        uint256 shadowedAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct Storage {
 | 
			
		||||
        mapping(address=>ShadowedAmount) shadowedBalances;
 | 
			
		||||
        mapping(address=>mapping(address=>ShadowedAmount)) shadowedAllowances;
 | 
			
		||||
        // When enabled the HackedERC20 token will shadow and track balances
 | 
			
		||||
        // when disabled (default) it will call the original implementation
 | 
			
		||||
        bool enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bytes32 private constant STORAGE_SLOT = 0x64fd48372774b9637ace5c8c7a951f04ea13c793935207f2eada5382a0ec82cb;
 | 
			
		||||
    GasOverhead private constant GAS_OVERHEAD = GasOverhead(0xDeF1000000000000000000000000000000001337);
 | 
			
		||||
 | 
			
		||||
    // HackedERC20 also has the overhead of being Delegated to from the replaced token
 | 
			
		||||
    // USDT->DelegateHackedERC20->HackedERC20
 | 
			
		||||
    uint256 private constant DELEGATE_CALL_OVERHEAD = 5000;
 | 
			
		||||
 | 
			
		||||
    receive() external payable {}
 | 
			
		||||
 | 
			
		||||
    fallback() payable external {
 | 
			
		||||
        bytes memory r = _forwardCallToImpl();
 | 
			
		||||
        assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function balanceOf(address owner)
 | 
			
		||||
        external
 | 
			
		||||
        /* view */
 | 
			
		||||
        returns (uint256 balance)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_isEnabled()) {
 | 
			
		||||
            bytes memory r = _forwardCallToImpl();
 | 
			
		||||
            assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
        }
 | 
			
		||||
        (ShadowedAmount memory sBal,) = _getSyncedBalance(owner);
 | 
			
		||||
        return sBal.shadowedAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function allowance(address owner, address spender)
 | 
			
		||||
        external
 | 
			
		||||
        /* view */
 | 
			
		||||
        returns (uint256 allowance_)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_isEnabled()) {
 | 
			
		||||
            bytes memory r = _forwardCallToImpl();
 | 
			
		||||
            assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
        }
 | 
			
		||||
        (ShadowedAmount memory sBal,) = _getSyncedAllowance(owner, spender);
 | 
			
		||||
        return sBal.shadowedAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function transferFrom(address from, address to, uint256 amount)
 | 
			
		||||
        public
 | 
			
		||||
        returns (bool success)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_isEnabled()) {
 | 
			
		||||
            bytes memory r = _forwardCallToImpl();
 | 
			
		||||
            assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
        }
 | 
			
		||||
        _updateAllowance(from, amount);
 | 
			
		||||
        success = _transferFromInternal(from, to, amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function transfer(address to, uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
        returns (bool success)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_isEnabled()) {
 | 
			
		||||
            bytes memory r = _forwardCallToImpl();
 | 
			
		||||
            assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
        }
 | 
			
		||||
        success = _transferFromInternal(msg.sender, to, amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function approve(address spender, uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        if (!_isEnabled()) {
 | 
			
		||||
            bytes memory r = _forwardCallToImpl();
 | 
			
		||||
            assembly { return(add(r, 32), mload(r)) }
 | 
			
		||||
        }
 | 
			
		||||
        (
 | 
			
		||||
            ShadowedAmount memory sAllowance,
 | 
			
		||||
        ) = _getSyncedAllowance(msg.sender, spender);
 | 
			
		||||
 | 
			
		||||
        sAllowance.shadowedAmount = amount;
 | 
			
		||||
        _writeSyncedAllowance(msg.sender, spender, sAllowance);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _setBalance(address owner, uint256 amount)
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        (ShadowedAmount memory sBal,) = _getSyncedBalance(owner);
 | 
			
		||||
        sBal.shadowedAmount = amount;
 | 
			
		||||
        _writeSyncedBalance(owner, sBal);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function _getSyncedAllowance(address owner, address spender)
 | 
			
		||||
        private
 | 
			
		||||
        /* view */
 | 
			
		||||
        returns (ShadowedAmount memory sAllowance, uint256 gasOverhead)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 trueAmount = abi.decode(
 | 
			
		||||
            _forwardCallToImpl(abi.encodeWithSelector(
 | 
			
		||||
                IERC20TokenV06.allowance.selector,
 | 
			
		||||
                owner,
 | 
			
		||||
                spender
 | 
			
		||||
            )),
 | 
			
		||||
            (uint256)
 | 
			
		||||
        );
 | 
			
		||||
        // We only want to measure the cost of the underlying token storage lookup
 | 
			
		||||
        // Not including the excess overhead of our shadow lookup
 | 
			
		||||
        uint256 gasBefore = gasleft();
 | 
			
		||||
        sAllowance = _getStorage().shadowedAllowances[owner][spender];
 | 
			
		||||
        _syncShadowedAmount(sAllowance, trueAmount);
 | 
			
		||||
        gasOverhead = gasBefore - gasleft();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getSyncedBalance(address owner)
 | 
			
		||||
        private
 | 
			
		||||
        returns (ShadowedAmount memory sBal, uint256 gasOverhead)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 trueAmount = abi.decode(
 | 
			
		||||
            _forwardCallToImpl(abi.encodeWithSelector(
 | 
			
		||||
                IERC20TokenV06.balanceOf.selector,
 | 
			
		||||
                owner
 | 
			
		||||
            )),
 | 
			
		||||
            (uint256)
 | 
			
		||||
        );
 | 
			
		||||
        // We only want to measure the cost of the underlying token storage lookup
 | 
			
		||||
        // Not including the excess overhead of our shadow lookup
 | 
			
		||||
        uint256 gasBefore = gasleft();
 | 
			
		||||
        sBal = _getStorage().shadowedBalances[owner];
 | 
			
		||||
        _syncShadowedAmount(sBal, trueAmount);
 | 
			
		||||
        gasOverhead = gasBefore - gasleft();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _syncShadowedAmount(ShadowedAmount memory sAmount, uint256 trueAmount)
 | 
			
		||||
        private
 | 
			
		||||
        pure
 | 
			
		||||
    {
 | 
			
		||||
        if (!sAmount.isShadowed) {
 | 
			
		||||
            sAmount.isShadowed = true;
 | 
			
		||||
            sAmount.shadowedAmount = trueAmount;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Detect balance changes that can occur from outside of ERC20
 | 
			
		||||
            // functions.
 | 
			
		||||
            if (sAmount.lastTrueAmount > trueAmount) {
 | 
			
		||||
                sAmount.shadowedAmount = _sub(
 | 
			
		||||
                    sAmount.lastTrueAmount,
 | 
			
		||||
                    sAmount.lastTrueAmount - trueAmount,
 | 
			
		||||
                    'HackedERC20/SHADOW_ADJUSTMENT_UNDERFLOW'
 | 
			
		||||
                );
 | 
			
		||||
            } else if (sAmount.lastTrueAmount < trueAmount) {
 | 
			
		||||
                sAmount.shadowedAmount = _add(
 | 
			
		||||
                    sAmount.lastTrueAmount,
 | 
			
		||||
                    trueAmount - sAmount.lastTrueAmount,
 | 
			
		||||
                    'HackedERC20/SHADOW_ADJUSTMENT_OVERFLOW'
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        sAmount.lastTrueAmount = trueAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _writeSyncedBalance(address owner, ShadowedAmount memory sBal)
 | 
			
		||||
        private
 | 
			
		||||
    {
 | 
			
		||||
        _getStorage().shadowedBalances[owner] = sBal;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _writeSyncedAllowance(
 | 
			
		||||
        address owner,
 | 
			
		||||
        address spender,
 | 
			
		||||
        ShadowedAmount memory sAllowance
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
    {
 | 
			
		||||
        _getStorage().shadowedAllowances[owner][spender] = sAllowance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getStorage() private pure returns (Storage storage st) {
 | 
			
		||||
        bytes32 slot = STORAGE_SLOT;
 | 
			
		||||
        assembly { st_slot := slot }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getOriginalImplementationAddress()
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (address impl)
 | 
			
		||||
    {
 | 
			
		||||
        return address(uint160(address(this)) + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _forwardCallToImpl()
 | 
			
		||||
        private
 | 
			
		||||
        returns (bytes memory resultData)
 | 
			
		||||
    {
 | 
			
		||||
        bool success;
 | 
			
		||||
        (success, resultData) =
 | 
			
		||||
            _getOriginalImplementationAddress().delegatecall(msg.data);
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            assembly { revert(add(resultData, 32), mload(resultData)) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _forwardCallToImpl(bytes memory callData)
 | 
			
		||||
        private
 | 
			
		||||
        returns (bytes memory resultData)
 | 
			
		||||
    {
 | 
			
		||||
        bool success;
 | 
			
		||||
        (success, resultData) =
 | 
			
		||||
            _getOriginalImplementationAddress().delegatecall(callData);
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            assembly { revert(add(resultData, 32), mload(resultData)) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _transferFromInternal(address from, address to, uint256 amount)
 | 
			
		||||
        internal
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        ShadowedAmount memory sFromBal;
 | 
			
		||||
        ShadowedAmount memory sToBal;
 | 
			
		||||
        uint256 gasOverhead;
 | 
			
		||||
        uint256 _gasOverhead;
 | 
			
		||||
 | 
			
		||||
        (sFromBal, _gasOverhead) = _getSyncedBalance(from);
 | 
			
		||||
        gasOverhead += _gasOverhead;
 | 
			
		||||
        sFromBal.shadowedAmount = _sub(
 | 
			
		||||
            sFromBal.shadowedAmount,
 | 
			
		||||
            amount,
 | 
			
		||||
            'HackedERC20/BALANCE_UNDERFLOW'
 | 
			
		||||
        );
 | 
			
		||||
        _writeSyncedBalance(from, sFromBal);
 | 
			
		||||
 | 
			
		||||
        (sToBal, _gasOverhead) = _getSyncedBalance(to);
 | 
			
		||||
        gasOverhead += _gasOverhead;
 | 
			
		||||
        sToBal.shadowedAmount = _add(
 | 
			
		||||
            sToBal.shadowedAmount,
 | 
			
		||||
            amount,
 | 
			
		||||
            'HackedERC20/BALANCE_OVERFLOW'
 | 
			
		||||
        );
 | 
			
		||||
        _writeSyncedBalance(to, sToBal);
 | 
			
		||||
 | 
			
		||||
        // Update the global gas overhead from a transfer call
 | 
			
		||||
        try
 | 
			
		||||
            GAS_OVERHEAD.addOverhead(gasOverhead + DELEGATE_CALL_OVERHEAD, gasleft())
 | 
			
		||||
        { } catch { }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _updateAllowance(address from, uint256 amount)
 | 
			
		||||
        internal
 | 
			
		||||
    {
 | 
			
		||||
        (ShadowedAmount memory sAllowance, uint256 gasOverhead) = _getSyncedAllowance(from, msg.sender);
 | 
			
		||||
        if (from != msg.sender && sAllowance.shadowedAmount != uint256(-1)) {
 | 
			
		||||
            sAllowance.shadowedAmount = _sub(
 | 
			
		||||
                sAllowance.shadowedAmount,
 | 
			
		||||
                amount,
 | 
			
		||||
                'HackedERC20/ALLOWANCE_UNDERFLOW'
 | 
			
		||||
            );
 | 
			
		||||
            _writeSyncedAllowance(from, msg.sender, sAllowance);
 | 
			
		||||
        }
 | 
			
		||||
        uint256 gasBefore = gasleft();
 | 
			
		||||
        // Assume a NON MAX_UINT results in allowance update SSTORE
 | 
			
		||||
        _writeSyncedAllowance(from, msg.sender, sAllowance);
 | 
			
		||||
        gasOverhead += gasBefore - gasleft();
 | 
			
		||||
        // Update the global gas overhead from a allowance check
 | 
			
		||||
        try
 | 
			
		||||
            GAS_OVERHEAD.addOverhead(gasOverhead + DELEGATE_CALL_OVERHEAD, gasleft())
 | 
			
		||||
        { } catch { }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _isEnabled()
 | 
			
		||||
        internal
 | 
			
		||||
        returns (bool)
 | 
			
		||||
    {
 | 
			
		||||
        return _getStorage().enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _setEnabled(bool enabled)
 | 
			
		||||
        public
 | 
			
		||||
    {
 | 
			
		||||
        _getStorage().enabled = enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _add(uint256 a, uint256 b, string memory errMsg)
 | 
			
		||||
        private
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256 c)
 | 
			
		||||
    {
 | 
			
		||||
        c = a + b;
 | 
			
		||||
        require(c >= a, errMsg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sub(uint256 a, uint256 b, string memory errMsg)
 | 
			
		||||
        private
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256 c)
 | 
			
		||||
    {
 | 
			
		||||
        c = a - b;
 | 
			
		||||
        require(c <= a, errMsg);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,6 +20,8 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
interface IKyberDmmFactory {
 | 
			
		||||
 | 
			
		||||
    function getPoolAtIndex(address token0, address token1, uint256 index)
 | 
			
		||||
@@ -28,33 +30,36 @@ interface IKyberDmmFactory {
 | 
			
		||||
        returns (address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IKyberDmmRouter {
 | 
			
		||||
 | 
			
		||||
    function factory() external view returns (address);
 | 
			
		||||
 | 
			
		||||
    function getAmountsOut(uint256 amountIn, address[] calldata pools, address[] calldata path)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory amounts);
 | 
			
		||||
 | 
			
		||||
    function getAmountsIn(uint256 amountOut, address[] calldata pools, address[] calldata path)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory amounts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract KyberDmmSampler
 | 
			
		||||
contract KyberDmmSampler is
 | 
			
		||||
    MixinKyberDmm,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for KyberDmm calls.
 | 
			
		||||
    uint256 constant private KYBER_DMM_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromKyberDmm(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeKyberDmm(
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from KyberDmm.
 | 
			
		||||
    /// @param router Router to look up tokens and amounts
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return pools The pool addresses involved in the multi path trade
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromKyberDmm(
 | 
			
		||||
@@ -63,32 +68,26 @@ contract KyberDmmSampler
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (address[] memory pools, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            address[] memory pools,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        pools = _getKyberDmmPools(router, path);
 | 
			
		||||
        if (pools.length == 0) {
 | 
			
		||||
            return (pools, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IKyberDmmRouter(router).getAmountsOut
 | 
			
		||||
                    {gas: KYBER_DMM_CALL_GAS}
 | 
			
		||||
                    (takerTokenAmounts[i], pools, path)
 | 
			
		||||
                returns (uint256[] memory amounts)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amounts[path.length - 1];
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            return (pools, gasUsed, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: path[0],
 | 
			
		||||
                buyToken: path[path.length - 1],
 | 
			
		||||
                bridgeData: abi.encode(router, pools, path),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromKyberDmm
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from KyberDmm.
 | 
			
		||||
@@ -96,6 +95,7 @@ contract KyberDmmSampler
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return pools The pool addresses involved in the multi path trade
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromKyberDmm(
 | 
			
		||||
@@ -104,32 +104,32 @@ contract KyberDmmSampler
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (address[] memory pools, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            address[] memory pools,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory takerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        pools = _getKyberDmmPools(router, path);
 | 
			
		||||
        if (pools.length == 0) {
 | 
			
		||||
            return (pools, takerTokenAmounts);
 | 
			
		||||
            return (pools, gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IKyberDmmRouter(router).getAmountsIn
 | 
			
		||||
                    {gas: KYBER_DMM_CALL_GAS}
 | 
			
		||||
                    (makerTokenAmounts[i], pools, path)
 | 
			
		||||
                returns (uint256[] memory amounts)
 | 
			
		||||
            {
 | 
			
		||||
                takerTokenAmounts[i] = amounts[0];
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (takerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        address[] memory reversedPath = new address[](path.length);
 | 
			
		||||
        for (uint256 i = 0; i < path.length; ++i) {
 | 
			
		||||
            reversedPath[i] = path[path.length - i - 1];
 | 
			
		||||
        }
 | 
			
		||||
        address[] memory reversedPools = _getKyberDmmPools(router, reversedPath);
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: path[0],
 | 
			
		||||
                buyToken: path[path.length - 1],
 | 
			
		||||
                sellTokenData: abi.encode(router, pools, path),
 | 
			
		||||
                buyTokenData: abi.encode(router, reversedPools, reversedPath),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromKyberDmm
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getKyberDmmPools(
 | 
			
		||||
 
 | 
			
		||||
@@ -21,18 +21,22 @@ pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IKyberNetwork.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinKyber.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract KyberSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinKyber,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// @dev Gas limit for Kyber calls.
 | 
			
		||||
    uint256 constant private KYBER_CALL_GAS = 500e3; // 500k
 | 
			
		||||
    /// @dev Kyber ETH pseudo-address.
 | 
			
		||||
    address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinKyber(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    struct KyberSamplerOpts {
 | 
			
		||||
        uint256 reserveOffset;
 | 
			
		||||
@@ -42,6 +46,26 @@ contract KyberSampler is
 | 
			
		||||
        bytes hint;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromKyber(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeKyberInternal(
 | 
			
		||||
            // these are Immutable in MixinKyber, since they are only set in constructor they must be passed in
 | 
			
		||||
            IERC20TokenV06(KYBER_ETH_ADDRESS),
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Kyber.
 | 
			
		||||
    /// @param opts KyberSamplerOpts The nth reserve
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
@@ -49,6 +73,7 @@ contract KyberSampler is
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return reserveId The id of the reserve found at reserveOffset
 | 
			
		||||
    /// @return hint The hint for the selected reserve
 | 
			
		||||
    /// @return gasUsed Gas consumed per sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token amount.
 | 
			
		||||
    function sampleSellsFromKyberNetwork(
 | 
			
		||||
        KyberSamplerOpts memory opts,
 | 
			
		||||
@@ -57,32 +82,29 @@ contract KyberSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes32 reserveId, bytes memory hint, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bytes32 reserveId,
 | 
			
		||||
            bytes memory hint,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        reserveId = _getNextReserveId(opts, takerToken, makerToken);
 | 
			
		||||
        if (reserveId == 0x0) {
 | 
			
		||||
            return (reserveId, hint, makerTokenAmounts);
 | 
			
		||||
            return (reserveId, hint, gasUsed, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
 | 
			
		||||
        hint = opts.hint;
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 value = this.sampleSellFromKyberNetwork(
 | 
			
		||||
                opts,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                makerToken,
 | 
			
		||||
                takerTokenAmounts[i]
 | 
			
		||||
            );
 | 
			
		||||
            makerTokenAmounts[i] = value;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(opts.networkProxy, hint),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromKyber
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Kyber.
 | 
			
		||||
@@ -92,6 +114,7 @@ contract KyberSampler is
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return reserveId The id of the reserve found at reserveOffset
 | 
			
		||||
    /// @return hint The hint for the selected reserve
 | 
			
		||||
    /// @return gasUsed Gas consumed for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token amount.
 | 
			
		||||
    function sampleBuysFromKyberNetwork(
 | 
			
		||||
        KyberSamplerOpts memory opts,
 | 
			
		||||
@@ -100,27 +123,30 @@ contract KyberSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes32 reserveId, bytes memory hint, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (
 | 
			
		||||
            bytes32 reserveId,
 | 
			
		||||
            bytes memory hint,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory takerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
 | 
			
		||||
        reserveId = _getNextReserveId(opts, takerToken, makerToken);
 | 
			
		||||
        if (reserveId == 0x0) {
 | 
			
		||||
            return (reserveId, hint, takerTokenAmounts);
 | 
			
		||||
            return (reserveId, hint, gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
        opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
 | 
			
		||||
        hint = opts.hint;
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, opts),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, opts),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(opts.networkProxy, hint),
 | 
			
		||||
                buyTokenData: abi.encode(opts.networkProxy, hint),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromKyber
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
        return (reserveId, hint, takerTokenAmounts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function encodeKyberHint(
 | 
			
		||||
@@ -201,73 +227,6 @@ contract KyberSampler is
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromKyber(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        (address makerToken, KyberSamplerOpts memory opts) =
 | 
			
		||||
            abi.decode(makerTokenData, (address, KyberSamplerOpts));
 | 
			
		||||
        (address takerToken, ) =
 | 
			
		||||
            abi.decode(takerTokenData, (address, KyberSamplerOpts));
 | 
			
		||||
        try
 | 
			
		||||
            this.sampleSellFromKyberNetwork
 | 
			
		||||
                (opts, takerToken, makerToken, sellAmount)
 | 
			
		||||
            returns (uint256 amount)
 | 
			
		||||
        {
 | 
			
		||||
            return amount;
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleSellFromKyberNetwork(
 | 
			
		||||
        KyberSamplerOpts memory opts,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 makerTokenAmount)
 | 
			
		||||
    {
 | 
			
		||||
        // If there is no hint do not continue
 | 
			
		||||
        if (opts.hint.length == 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
            IKyberNetworkProxy(opts.networkProxy).getExpectedRateAfterFee
 | 
			
		||||
                {gas: KYBER_CALL_GAS}
 | 
			
		||||
                (
 | 
			
		||||
                    takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
 | 
			
		||||
                    makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
 | 
			
		||||
                    takerTokenAmount,
 | 
			
		||||
                    0, // fee
 | 
			
		||||
                    opts.hint
 | 
			
		||||
                )
 | 
			
		||||
            returns (uint256 rate)
 | 
			
		||||
        {
 | 
			
		||||
            uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
 | 
			
		||||
            uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
 | 
			
		||||
            makerTokenAmount =
 | 
			
		||||
                rate *
 | 
			
		||||
                takerTokenAmount *
 | 
			
		||||
                10 ** makerTokenDecimals /
 | 
			
		||||
                10 ** takerTokenDecimals /
 | 
			
		||||
                10 ** 18;
 | 
			
		||||
            return makerTokenAmount;
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getNextReserveId(
 | 
			
		||||
        KyberSamplerOpts memory opts,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,72 +20,84 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinLido.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract LidoSampler is SamplerUtils {
 | 
			
		||||
    struct LidoInfo {
 | 
			
		||||
        address stEthToken;
 | 
			
		||||
        address wethToken;
 | 
			
		||||
contract LidoSampler is
 | 
			
		||||
    MixinLido,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinLido(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromLido(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeLidoInternal(
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Lido
 | 
			
		||||
    /// @param lidoInfo Info regarding a specific Lido deployment
 | 
			
		||||
    /// @param lido Address of the Lido contract
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed for each sample amount
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromLido(
 | 
			
		||||
        LidoInfo memory lidoInfo,
 | 
			
		||||
        address lido,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256[] memory)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
 | 
			
		||||
        if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
 | 
			
		||||
            // Return 0 values if not selling WETH for stETH
 | 
			
		||||
            uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
            uint256[] memory makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Minting stETH is always 1:1 therefore we can just return the same amounts back
 | 
			
		||||
        return takerTokenAmounts;
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(lido),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromLido
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Lido.
 | 
			
		||||
    /// @param lidoInfo Info regarding a specific Lido deployment
 | 
			
		||||
    /// @param lido Address of the Lido contract
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed for each sample amount
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromLido(
 | 
			
		||||
        LidoInfo memory lidoInfo,
 | 
			
		||||
        address lido,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256[] memory)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
 | 
			
		||||
        if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
 | 
			
		||||
            // Return 0 values if not buying stETH for WETH
 | 
			
		||||
            uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
            uint256[] memory takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Minting stETH is always 1:1 therefore we can just return the same amounts back
 | 
			
		||||
        return makerTokenAmounts;
 | 
			
		||||
        // 1:1 rate so we can perform an WETH sell
 | 
			
		||||
        return this.sampleSellsFromLido(lido, takerToken, makerToken, makerTokenAmounts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,24 +20,38 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/vendor/ILiquidityProvider.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinZeroExBridge.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract LiquidityProviderSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinZeroExBridge,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Default gas limit for liquidity provider calls.
 | 
			
		||||
    uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromLiquidityProvider(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeZeroExBridge(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
    /// @param providerAddress Address of the liquidity provider.
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromLiquidityProvider(
 | 
			
		||||
@@ -47,34 +61,18 @@ contract LiquidityProviderSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        // Initialize array of maker token amounts.
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                ILiquidityProvider(providerAddress).getSellQuote
 | 
			
		||||
                    {gas: DEFAULT_CALL_GAS}
 | 
			
		||||
                    (
 | 
			
		||||
                        IERC20TokenV06(takerToken),
 | 
			
		||||
                        IERC20TokenV06(makerToken),
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    )
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        bytes memory lpData;
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(providerAddress, lpData),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromLiquidityProvider
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
@@ -82,6 +80,7 @@ contract LiquidityProviderSampler is
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromLiquidityProvider(
 | 
			
		||||
@@ -91,42 +90,18 @@ contract LiquidityProviderSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        takerTokenAmounts = _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, providerAddress),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, providerAddress),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProvider
 | 
			
		||||
        bytes memory lpData;
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(providerAddress, lpData),
 | 
			
		||||
                buyTokenData: abi.encode(providerAddress, lpData),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromLiquidityProvider
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromLiquidityProvider(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address providerAddress) =
 | 
			
		||||
            abi.decode(takerTokenData, (address, address));
 | 
			
		||||
        (address makerToken) =
 | 
			
		||||
            abi.decode(makerTokenData, (address));
 | 
			
		||||
        try
 | 
			
		||||
            this.sampleSellsFromLiquidityProvider
 | 
			
		||||
                {gas: DEFAULT_CALL_GAS}
 | 
			
		||||
                (providerAddress, takerToken, makerToken, _toSingleValueArray(sellAmount))
 | 
			
		||||
            returns (uint256[] memory amounts)
 | 
			
		||||
        {
 | 
			
		||||
            return amounts[0];
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,23 +20,38 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IMStable.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMStable.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract MStableSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinMStable,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Default gas limit for mStable calls.
 | 
			
		||||
    uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromMStable(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeMStable(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from the mStable contract
 | 
			
		||||
    /// @param router Address of the mStable contract
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromMStable(
 | 
			
		||||
@@ -46,31 +61,17 @@ contract MStableSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        // Initialize array of maker token amounts.
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IMStable(router).getSwapOutput
 | 
			
		||||
                    {gas: DEFAULT_CALL_GAS}
 | 
			
		||||
                    (takerToken, makerToken, takerTokenAmounts[i])
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMStable
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from MStable contract
 | 
			
		||||
@@ -78,6 +79,7 @@ contract MStableSampler is
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromMStable(
 | 
			
		||||
@@ -87,41 +89,17 @@ contract MStableSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        return _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, router),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, router),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(router),
 | 
			
		||||
                buyTokenData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMStable
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromMStable(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address router) =
 | 
			
		||||
            abi.decode(takerTokenData, (address, address));
 | 
			
		||||
        (address makerToken) =
 | 
			
		||||
            abi.decode(makerTokenData, (address));
 | 
			
		||||
        try
 | 
			
		||||
            this.sampleSellsFromMStable
 | 
			
		||||
                (router, takerToken, makerToken, _toSingleValueArray(sellAmount))
 | 
			
		||||
            returns (uint256[] memory amounts)
 | 
			
		||||
        {
 | 
			
		||||
            return amounts[0];
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,69 +20,13 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
 | 
			
		||||
 | 
			
		||||
interface IPSM {
 | 
			
		||||
    // @dev Get the fee for selling USDC to DAI in PSM
 | 
			
		||||
    // @return tin toll in [wad]
 | 
			
		||||
    function tin() external view returns (uint256);
 | 
			
		||||
    // @dev Get the fee for selling DAI to USDC in PSM
 | 
			
		||||
    // @return tout toll out [wad]
 | 
			
		||||
    function tout() external view returns (uint256);
 | 
			
		||||
 | 
			
		||||
    // @dev Get the address of the PSM state Vat
 | 
			
		||||
    // @return address of the Vat
 | 
			
		||||
    function vat() external view returns (address);
 | 
			
		||||
 | 
			
		||||
    // @dev Get the address of the underlying vault powering PSM
 | 
			
		||||
    // @return address of gemJoin contract
 | 
			
		||||
    function gemJoin() external view returns (address);
 | 
			
		||||
 | 
			
		||||
    // @dev Get the address of DAI
 | 
			
		||||
    // @return address of DAI contract
 | 
			
		||||
    function dai() external view returns (address);
 | 
			
		||||
 | 
			
		||||
    // @dev Sell USDC for DAI
 | 
			
		||||
    // @param usr The address of the account trading USDC for DAI.
 | 
			
		||||
    // @param gemAmt The amount of USDC to sell in USDC base units
 | 
			
		||||
    function sellGem(
 | 
			
		||||
        address usr,
 | 
			
		||||
        uint256 gemAmt
 | 
			
		||||
    ) external;
 | 
			
		||||
    // @dev Buy USDC for DAI
 | 
			
		||||
    // @param usr The address of the account trading DAI for USDC
 | 
			
		||||
    // @param gemAmt The amount of USDC to buy in USDC base units
 | 
			
		||||
    function buyGem(
 | 
			
		||||
        address usr,
 | 
			
		||||
        uint256 gemAmt
 | 
			
		||||
    ) external;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IVAT {
 | 
			
		||||
    // @dev Get a collateral type by identifier
 | 
			
		||||
    // @param ilkIdentifier bytes32 identifier. Example: ethers.utils.formatBytes32String("PSM-USDC-A")
 | 
			
		||||
    // @return ilk
 | 
			
		||||
    // @return ilk.Art Total Normalised Debt in wad
 | 
			
		||||
    // @return ilk.rate Accumulated Rates in ray
 | 
			
		||||
    // @return ilk.spot Price with Safety Margin in ray
 | 
			
		||||
    // @return ilk.line Debt Ceiling in rad
 | 
			
		||||
    // @return ilk.dust Urn Debt Floor in rad
 | 
			
		||||
    function ilks(
 | 
			
		||||
        bytes32 ilkIdentifier
 | 
			
		||||
    ) external view returns (
 | 
			
		||||
        uint256 Art,
 | 
			
		||||
        uint256 rate,
 | 
			
		||||
        uint256 spot,
 | 
			
		||||
        uint256 line,
 | 
			
		||||
        uint256 dust
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract MakerPSMSampler is
 | 
			
		||||
    SamplerUtils
 | 
			
		||||
    MixinMakerPSM,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    using LibSafeMathV06 for uint256;
 | 
			
		||||
 | 
			
		||||
    /// @dev Information about which PSM module to use
 | 
			
		||||
    struct MakerPsmInfo {
 | 
			
		||||
@@ -91,18 +35,22 @@ contract MakerPSMSampler is
 | 
			
		||||
        address gemTokenAddress;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Gas limit for MakerPsm calls.
 | 
			
		||||
    uint256 constant private MAKER_PSM_CALL_GAS = 300e3; // 300k
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Maker units
 | 
			
		||||
    // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)
 | 
			
		||||
    uint256 constant private WAD = 10 ** 18;
 | 
			
		||||
    // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios)
 | 
			
		||||
    uint256 constant private RAY = 10 ** 27;
 | 
			
		||||
    // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray)
 | 
			
		||||
    uint256 constant private RAD = 10 ** 45;
 | 
			
		||||
    // See https://github.com/makerdao/dss/blob/master/DEVELOPING.m
 | 
			
		||||
    function sampleSwapFromMakerPsm(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeMakerPsm(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Maker PSM
 | 
			
		||||
    function sampleSellsFromMakerPsm(
 | 
			
		||||
@@ -112,29 +60,22 @@ contract MakerPSMSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        IPSM psm = IPSM(psmInfo.psmAddress);
 | 
			
		||||
        IVAT vat = IVAT(psm.vat());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        if (makerToken != psm.dai() && takerToken != psm.dai()) {
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 buyAmount = _samplePSMSell(psmInfo, makerToken, takerToken, takerTokenAmounts[i], psm, vat);
 | 
			
		||||
 | 
			
		||||
            if (buyAmount == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(
 | 
			
		||||
                    MakerPsmBridgeData({
 | 
			
		||||
                        psmAddress: psmInfo.psmAddress,
 | 
			
		||||
                        gemTokenAddres: psmInfo.gemTokenAddress
 | 
			
		||||
                    })
 | 
			
		||||
                ),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMakerPsm
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleBuysFromMakerPsm(
 | 
			
		||||
@@ -144,124 +85,21 @@ contract MakerPSMSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        IPSM psm = IPSM(psmInfo.psmAddress);
 | 
			
		||||
        IVAT vat = IVAT(psm.vat());
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        if (makerToken != psm.dai() && takerToken != psm.dai()) {
 | 
			
		||||
            return takerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 sellAmount = _samplePSMBuy(psmInfo, makerToken, takerToken, makerTokenAmounts[i], psm, vat);
 | 
			
		||||
 | 
			
		||||
            if (sellAmount == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            takerTokenAmounts[i] = sellAmount;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        MakerPsmBridgeData memory data = MakerPsmBridgeData({
 | 
			
		||||
            psmAddress: psmInfo.psmAddress,
 | 
			
		||||
            gemTokenAddres: psmInfo.gemTokenAddress
 | 
			
		||||
        });
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(data),
 | 
			
		||||
                buyTokenData: abi.encode(data),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMakerPsm
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _samplePSMSell(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 takerTokenAmount, IPSM psm, IVAT vat)
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
 | 
			
		||||
        uint256 gemTokenBaseUnit = uint256(1e6);
 | 
			
		||||
 | 
			
		||||
        if (takerToken == psmInfo.gemTokenAddress) {
 | 
			
		||||
            // Simulate sellGem
 | 
			
		||||
            // Selling USDC to the PSM, increasing the total debt
 | 
			
		||||
            // Convert USDC 6 decimals to 18 decimals [wad]
 | 
			
		||||
            uint256 takerTokenAmountInWad = takerTokenAmount.safeMul(1e12);
 | 
			
		||||
 | 
			
		||||
            uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
 | 
			
		||||
 | 
			
		||||
            // PSM is too full to fit
 | 
			
		||||
            if (newTotalDebtInRad >= debtCeilingInRad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint256 feeInWad = takerTokenAmountInWad.safeMul(psm.tin()).safeDiv(WAD);
 | 
			
		||||
            uint256 makerTokenAmountInWad = takerTokenAmountInWad.safeSub(feeInWad);
 | 
			
		||||
 | 
			
		||||
            return makerTokenAmountInWad;
 | 
			
		||||
        } else if (makerToken == psmInfo.gemTokenAddress) {
 | 
			
		||||
            // Simulate buyGem
 | 
			
		||||
            // Buying USDC from the PSM, decreasing the total debt
 | 
			
		||||
            // Selling DAI for USDC, already in 18 decimals [wad]
 | 
			
		||||
            uint256 takerTokenAmountInWad = takerTokenAmount;
 | 
			
		||||
            if (takerTokenAmountInWad > totalDebtInWad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
 | 
			
		||||
 | 
			
		||||
            // PSM is empty, not enough USDC to buy from it
 | 
			
		||||
            if (newTotalDebtInRad <= debtFloorInRad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint256 feeDivisorInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
 | 
			
		||||
            uint256 makerTokenAmountInGemTokenBaseUnits =  takerTokenAmountInWad.safeMul(gemTokenBaseUnit).safeDiv(feeDivisorInWad);
 | 
			
		||||
 | 
			
		||||
            return makerTokenAmountInGemTokenBaseUnits;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _samplePSMBuy(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 makerTokenAmount, IPSM psm, IVAT vat)
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        (uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
 | 
			
		||||
 | 
			
		||||
        if (takerToken == psmInfo.gemTokenAddress) {
 | 
			
		||||
            // Simulate sellGem
 | 
			
		||||
            // Selling USDC to the PSM, increasing the total debt
 | 
			
		||||
            uint256 makerTokenAmountInWad = makerTokenAmount;
 | 
			
		||||
            uint256 feeDivisorInWad = WAD.safeSub(psm.tin()); // eg. 0.999 * 10 ** 18 with 0.1% tin;
 | 
			
		||||
            uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(WAD).safeDiv(feeDivisorInWad);
 | 
			
		||||
            uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
 | 
			
		||||
 | 
			
		||||
            // PSM is too full to fit
 | 
			
		||||
            if (newTotalDebtInRad >= debtCeilingInRad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            uint256 takerTokenAmountInGemInGemBaseUnits = (takerTokenAmountInWad.safeDiv(1e12)).safeAdd(1); // Add 1 to deal with cut off decimals converting to lower decimals
 | 
			
		||||
 | 
			
		||||
            return takerTokenAmountInGemInGemBaseUnits;
 | 
			
		||||
        } else if (makerToken == psmInfo.gemTokenAddress) {
 | 
			
		||||
            // Simulate buyGem
 | 
			
		||||
            // Buying USDC from the PSM, decreasing the total debt
 | 
			
		||||
            uint256 makerTokenAmountInWad = makerTokenAmount.safeMul(1e12);
 | 
			
		||||
            uint256 feeMultiplierInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
 | 
			
		||||
            uint256 takerTokenAmountInWad =  makerTokenAmountInWad.safeMul(feeMultiplierInWad).safeDiv(WAD);
 | 
			
		||||
            if (takerTokenAmountInWad > totalDebtInWad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
 | 
			
		||||
 | 
			
		||||
            // PSM is empty, not enough USDC to buy
 | 
			
		||||
            if (newTotalDebtInRad <= debtFloorInRad) {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            return takerTokenAmountInWad;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,17 +20,40 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IMooniswap.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMooniswap.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
interface IMooniswapRegistry {
 | 
			
		||||
    function pools(address token1, address token2) external view returns(address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract MooniswapSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinMooniswap,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for Mooniswap calls.
 | 
			
		||||
    uint256 constant private MOONISWAP_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinMooniswap(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromMooniswap(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeMooniswapInternal(
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Mooniswap.
 | 
			
		||||
    /// @param registry Address of the Mooniswap Registry.
 | 
			
		||||
@@ -38,6 +61,7 @@ contract MooniswapSampler is
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return pool The contract address for the pool
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromMooniswap(
 | 
			
		||||
@@ -47,69 +71,22 @@ contract MooniswapSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (IMooniswap pool, uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (address pool, uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            uint256 buyAmount = sampleSingleSellFromMooniswapPool(
 | 
			
		||||
                registry,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                makerToken,
 | 
			
		||||
                takerTokenAmounts[i]
 | 
			
		||||
            );
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pool = IMooniswap(
 | 
			
		||||
            IMooniswapRegistry(registry).pools(takerToken, makerToken)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sampleSingleSellFromMooniswapPool(
 | 
			
		||||
        address registry,
 | 
			
		||||
        address mooniswapTakerToken,
 | 
			
		||||
        address mooniswapMakerToken,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        // Find the pool for the pair.
 | 
			
		||||
        IMooniswap pool = IMooniswap(
 | 
			
		||||
            IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken)
 | 
			
		||||
        );
 | 
			
		||||
        // If there is no pool then return early
 | 
			
		||||
        pool = _getMooniswapPool(registry, takerToken, makerToken);
 | 
			
		||||
        if (address(pool) == address(0)) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        uint256 poolBalance = mooniswapTakerToken == address(0)
 | 
			
		||||
            ? address(pool).balance
 | 
			
		||||
            : IERC20TokenV06(mooniswapTakerToken).balanceOf(address(pool));
 | 
			
		||||
        // If the pool balance is smaller than the sell amount
 | 
			
		||||
        // don't sample to avoid multiplication overflow in buys
 | 
			
		||||
        if (poolBalance < takerTokenAmount) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        try
 | 
			
		||||
            pool.getReturn
 | 
			
		||||
                {gas: MOONISWAP_CALL_GAS}
 | 
			
		||||
                (mooniswapTakerToken, mooniswapMakerToken, takerTokenAmount)
 | 
			
		||||
            returns (uint256 amount)
 | 
			
		||||
        {
 | 
			
		||||
            return amount;
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
            return (pool, gasUsed, makerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(pool),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMooniswap
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Mooniswap.
 | 
			
		||||
@@ -118,6 +95,7 @@ contract MooniswapSampler is
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token sell amount for each sample.
 | 
			
		||||
    /// @return pool The contract address for the pool
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromMooniswap(
 | 
			
		||||
@@ -127,43 +105,42 @@ contract MooniswapSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (IMooniswap pool, uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (address pool, uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        pool = _getMooniswapPool(registry, takerToken, makerToken);
 | 
			
		||||
        if (address(pool) == address(0)) {
 | 
			
		||||
            return (pool, gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(registry, makerToken),
 | 
			
		||||
                takerTokenData: abi.encode(registry, takerToken),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(pool),
 | 
			
		||||
                buyTokenData: abi.encode(pool),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromMooniswap
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        pool = IMooniswap(
 | 
			
		||||
            IMooniswapRegistry(registry).pools(takerToken, makerToken)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromMooniswap(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    function _getMooniswapPool(
 | 
			
		||||
        address registry,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
        internal
 | 
			
		||||
        returns (address pool)
 | 
			
		||||
    {
 | 
			
		||||
        (address registry, address mooniswapTakerToken) = abi.decode(takerTokenData, (address, address));
 | 
			
		||||
        (address _registry, address mooniswapMakerToken) = abi.decode(makerTokenData, (address, address));
 | 
			
		||||
        return sampleSingleSellFromMooniswapPool(
 | 
			
		||||
            registry,
 | 
			
		||||
            mooniswapTakerToken,
 | 
			
		||||
            mooniswapMakerToken,
 | 
			
		||||
            sellAmount
 | 
			
		||||
        );
 | 
			
		||||
        // WETH is actually ETH in these pools and represented as address(0)
 | 
			
		||||
        address _takerToken = takerToken == address(_getNativeWrappedToken()) ? address(0) : takerToken;
 | 
			
		||||
        address _makerToken = makerToken == address(_getNativeWrappedToken()) ? address(0) : makerToken;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
            IMooniswapRegistry(registry).pools{gas: 300e3}(_takerToken, _makerToken)
 | 
			
		||||
            returns (address _pool)
 | 
			
		||||
        {
 | 
			
		||||
            pool = _pool;
 | 
			
		||||
        } catch { }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,82 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IMultiBridge.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract MultiBridgeSampler {
 | 
			
		||||
 | 
			
		||||
    /// @dev Default gas limit for multibridge calls.
 | 
			
		||||
    uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from MultiBridge.
 | 
			
		||||
    /// @param multibridge Address of the MultiBridge contract.
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param intermediateToken The address of the intermediate token to
 | 
			
		||||
    ///        use in an indirect route.
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromMultiBridge(
 | 
			
		||||
        address multibridge,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address intermediateToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        // Initialize array of maker token amounts.
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        // If no address provided, return all zeros.
 | 
			
		||||
        if (multibridge == address(0)) {
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        IMultiBridge(0).getSellQuote.selector,
 | 
			
		||||
                        takerToken,
 | 
			
		||||
                        intermediateToken,
 | 
			
		||||
                        makerToken,
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            }
 | 
			
		||||
            // Exit early if the amount is too high for the source to serve
 | 
			
		||||
            if (buyAmount == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract SamplerUtils {
 | 
			
		||||
 | 
			
		||||
    /// @dev Overridable way to get token decimals.
 | 
			
		||||
    /// @param tokenAddress Address of the token.
 | 
			
		||||
    /// @return decimals The decimal places for the token.
 | 
			
		||||
    function _getTokenDecimals(address tokenAddress)
 | 
			
		||||
        virtual
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint8 decimals)
 | 
			
		||||
    {
 | 
			
		||||
        return LibERC20TokenV06.compatDecimals(IERC20TokenV06(tokenAddress));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _toSingleValueArray(uint256 v)
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256[] memory arr)
 | 
			
		||||
    {
 | 
			
		||||
        arr = new uint256[](1);
 | 
			
		||||
        arr[0] = v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Assert that the tokens in a trade pair are valid.
 | 
			
		||||
    /// @param makerToken Address of the maker token.
 | 
			
		||||
    /// @param takerToken Address of the taker token.
 | 
			
		||||
    function _assertValidPair(address makerToken, address takerToken)
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
    {
 | 
			
		||||
        require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -20,28 +20,41 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./interfaces/IShell.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinShell.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract ShellSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
    MixinShell,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    struct ShellInfo {
 | 
			
		||||
        address poolAddress;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Default gas limit for Shell calls.
 | 
			
		||||
    uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k
 | 
			
		||||
    function sampleSwapFromShell(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeShell(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from the Shell pool contract
 | 
			
		||||
    /// @param pool Address of the Shell pool contract
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromShell(
 | 
			
		||||
@@ -51,26 +64,17 @@ contract ShellSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        // Initialize array of maker token amounts.
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IShell(pool).viewOriginSwap
 | 
			
		||||
                    {gas: DEFAULT_CALL_GAS}
 | 
			
		||||
                    (takerToken, makerToken, takerTokenAmounts[i])
 | 
			
		||||
                returns (uint256 amount)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amount;
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(pool),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromShell
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Shell pool contract
 | 
			
		||||
@@ -78,6 +82,7 @@ contract ShellSampler is
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromShell(
 | 
			
		||||
@@ -87,40 +92,17 @@ contract ShellSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        return _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, pool),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, pool),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromShell
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(pool),
 | 
			
		||||
                buyTokenData: abi.encode(pool),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromShell
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromShell(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address pool) = abi.decode(takerTokenData, (address, address));
 | 
			
		||||
        (address makerToken) = abi.decode(makerTokenData, (address));
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
            this.sampleSellsFromShell
 | 
			
		||||
                (pool, takerToken, makerToken, _toSingleValueArray(sellAmount))
 | 
			
		||||
            returns (uint256[] memory amounts)
 | 
			
		||||
        {
 | 
			
		||||
            return amounts[0];
 | 
			
		||||
        } catch (bytes memory) {
 | 
			
		||||
            // Swallow failures, leaving all results as zero.
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,156 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
// import "./interfaces/ISmoothy.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
import "./interfaces/ISmoothy.sol";
 | 
			
		||||
 | 
			
		||||
contract SmoothySampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Information for sampling from smoothy sources.
 | 
			
		||||
    struct SmoothyInfo {
 | 
			
		||||
        address poolAddress;
 | 
			
		||||
        bytes4 sellQuoteFunctionSelector;
 | 
			
		||||
        bytes4 buyQuoteFunctionSelector;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Base gas limit for Smoothy calls.
 | 
			
		||||
    uint256 constant private SMOOTHY_CALL_GAS = 600e3;
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Smoothy.
 | 
			
		||||
    /// @param smoothyInfo Smoothy information specific to this token pair.
 | 
			
		||||
    /// @param fromTokenIdx Index of the taker token (what to sell).
 | 
			
		||||
    /// @param toTokenIdx Index of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromSmoothy(
 | 
			
		||||
        SmoothyInfo memory smoothyInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        // Basically a Curve fork
 | 
			
		||||
 | 
			
		||||
        // Smoothy only keep a percentage of its tokens available in reserve
 | 
			
		||||
        uint256 poolReserveMakerAmount = ISmoothy(smoothyInfo.poolAddress).getBalance(uint256(toTokenIdx)) -
 | 
			
		||||
                                         ISmoothy(smoothyInfo.poolAddress)._yBalances(uint256(toTokenIdx));
 | 
			
		||||
        (, , , uint256 decimals) = ISmoothy(smoothyInfo.poolAddress).getTokenStats(uint256(toTokenIdx));
 | 
			
		||||
        poolReserveMakerAmount = poolReserveMakerAmount/(10**(18-decimals));
 | 
			
		||||
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                smoothyInfo.poolAddress.staticcall.gas(SMOOTHY_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        smoothyInfo.sellQuoteFunctionSelector,
 | 
			
		||||
                        fromTokenIdx,
 | 
			
		||||
                        toTokenIdx,
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Make sure the quoted buyAmount is available in the pool reserve
 | 
			
		||||
            if (buyAmount >= poolReserveMakerAmount) {
 | 
			
		||||
                // Assign pool reserve amount for all higher samples to break early
 | 
			
		||||
                for (uint256 j = i; j < numSamples; j++) {
 | 
			
		||||
                    makerTokenAmounts[j] = poolReserveMakerAmount;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            } else {
 | 
			
		||||
                makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Break early if there are 0 amounts
 | 
			
		||||
            if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Smoothy.
 | 
			
		||||
    /// @param smoothyInfo Smoothy information specific to this token pair.
 | 
			
		||||
    /// @param fromTokenIdx Index of the taker token (what to sell).
 | 
			
		||||
    /// @param toTokenIdx Index of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromSmoothy(
 | 
			
		||||
        SmoothyInfo memory smoothyInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        // Buys not supported so approximate it.
 | 
			
		||||
        return _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(toTokenIdx, smoothyInfo),
 | 
			
		||||
                takerTokenData: abi.encode(fromTokenIdx, smoothyInfo),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromSmoothy
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromSmoothy(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (int128 takerTokenIdx, SmoothyInfo memory smoothyInfo) =
 | 
			
		||||
            abi.decode(takerTokenData, (int128, SmoothyInfo));
 | 
			
		||||
        (int128 makerTokenIdx) =
 | 
			
		||||
            abi.decode(makerTokenData, (int128));
 | 
			
		||||
        (bool success, bytes memory resultData) =
 | 
			
		||||
            address(this).staticcall(abi.encodeWithSelector(
 | 
			
		||||
                this.sampleSellsFromSmoothy.selector,
 | 
			
		||||
                smoothyInfo,
 | 
			
		||||
                takerTokenIdx,
 | 
			
		||||
                makerTokenIdx,
 | 
			
		||||
                _toSingleValueArray(sellAmount)
 | 
			
		||||
            ));
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // solhint-disable-next-line indent
 | 
			
		||||
        return abi.decode(resultData, (uint256[]))[0];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										412
									
								
								packages/asset-swapper/contracts/src/SwapRevertSampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								packages/asset-swapper/contracts/src/SwapRevertSampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,412 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2021 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.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./GasOverhead.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
 | 
			
		||||
 | 
			
		||||
interface IHackedERC20 {
 | 
			
		||||
    function _setBalance(address owner, uint256 amount) external;
 | 
			
		||||
    function _setEnabled(bool enabled) external;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract SwapRevertSampler {
 | 
			
		||||
    using LibRichErrorsV06 for bytes;
 | 
			
		||||
 | 
			
		||||
    /// @dev Fixed address to register and read Gas overhead introduced by Swap revert sampling
 | 
			
		||||
    GasOverhead private constant GAS_OVERHEAD = GasOverhead(0xDeF1000000000000000000000000000000001337);
 | 
			
		||||
    /// @dev Maximum approximate (positive) error rate when approximating a buy quote.
 | 
			
		||||
    uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
 | 
			
		||||
    /// @dev Maximum iterations to perform when approximating a buy quote.
 | 
			
		||||
    uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 3;
 | 
			
		||||
    uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
 | 
			
		||||
    /// @dev Upper limit of gas to give to a single Swap call
 | 
			
		||||
    uint256 private constant CALL_STIPEND = 2e6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // solhint-disable no-empty-blocks
 | 
			
		||||
    /// @dev Payable fallback to receive ETH from Kyber/WETH.
 | 
			
		||||
    receive ()
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    struct SwapRevertSamplerQuoteOpts {
 | 
			
		||||
        // Address of the token which is being sold
 | 
			
		||||
        address sellToken;
 | 
			
		||||
        // Address of the token which is wanted
 | 
			
		||||
        address buyToken;
 | 
			
		||||
        // Data required for the bridge to execute the swap
 | 
			
		||||
        bytes bridgeData;
 | 
			
		||||
        // Callback to retrieve a swap quote.
 | 
			
		||||
        function (address sellToken, address buyToken, bytes memory bridgeData, uint256 sellAmount)
 | 
			
		||||
            external
 | 
			
		||||
            returns (uint256)
 | 
			
		||||
            getSwapQuoteCallback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct SwapRevertSamplerBuyQuoteOpts {
 | 
			
		||||
        // Address of the token which is being sold
 | 
			
		||||
        address sellToken;
 | 
			
		||||
        // Address of the token which is wanted
 | 
			
		||||
        address buyToken;
 | 
			
		||||
        // Data required for the bridge to execute the SELL_TOKEN->BUY_TOKEN swap
 | 
			
		||||
        bytes sellTokenData;
 | 
			
		||||
        // Data required for the bridge to execute the BUY_TOKEN->SELL_TOKEN swap
 | 
			
		||||
        bytes buyTokenData;
 | 
			
		||||
        // Callback to retrieve a swap quote.
 | 
			
		||||
        function (address sellToken, address buyToken, bytes memory bridgeData, uint256 sellAmount)
 | 
			
		||||
            external
 | 
			
		||||
            returns (uint256)
 | 
			
		||||
            getSwapQuoteCallback;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _callRevert(
 | 
			
		||||
        bytes4 selector,
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 amountIn
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        // Clear any registered overhead
 | 
			
		||||
        try
 | 
			
		||||
            GAS_OVERHEAD.clearOverhead()
 | 
			
		||||
        { } catch { }
 | 
			
		||||
        // Measure the gas
 | 
			
		||||
        uint256 gasUsed = gasleft();
 | 
			
		||||
        // Perform the sell
 | 
			
		||||
        (bool success, bytes memory data) = address(this).call(
 | 
			
		||||
            abi.encodeWithSelector(selector, sellToken, buyToken, bridgeData, amountIn)
 | 
			
		||||
        );
 | 
			
		||||
        gasUsed = gasUsed - gasleft();
 | 
			
		||||
        // Remove any registered gas overhead
 | 
			
		||||
        try
 | 
			
		||||
            GAS_OVERHEAD.overhead()
 | 
			
		||||
            returns (uint256 gasOverhead)
 | 
			
		||||
        {
 | 
			
		||||
            gasUsed = gasUsed - gasOverhead;
 | 
			
		||||
        } catch { }
 | 
			
		||||
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            data.rrevert();
 | 
			
		||||
        }
 | 
			
		||||
        // Revert with the amount bought
 | 
			
		||||
        _revertSingleSwapSample(abi.decode(data, (uint256)), gasUsed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev  Mints the sell token, then performs the swap, then reverts with the amount out.
 | 
			
		||||
    /// The SwapRevertSamplerQuoteOpts has been unrolled here as our ABI encoder cannot support
 | 
			
		||||
    /// encoding the function
 | 
			
		||||
    function _mintCallRevert(
 | 
			
		||||
        bytes4 selector,
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256[] memory amountsIn
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        // We assume the amounts are ascending and that
 | 
			
		||||
        // the underlying call can handle selling a specific amount
 | 
			
		||||
        uint256 amountIn = amountsIn[amountsIn.length - 1];
 | 
			
		||||
 | 
			
		||||
        if (sellToken == address(_getNativeWrappedToken())) {
 | 
			
		||||
            try
 | 
			
		||||
                IEtherTokenV06(payable(sellToken)).deposit{ value: amountIn }()
 | 
			
		||||
            { } catch { }
 | 
			
		||||
        } else {
 | 
			
		||||
            IHackedERC20 hackedSellToken = IHackedERC20(payable(sellToken));
 | 
			
		||||
            // Enable sell token to be tracked and shadowed
 | 
			
		||||
            try
 | 
			
		||||
                hackedSellToken._setEnabled(true)
 | 
			
		||||
            { } catch { }
 | 
			
		||||
 | 
			
		||||
            // Mint enough to sell
 | 
			
		||||
            try
 | 
			
		||||
                hackedSellToken._setBalance(address(this), amountIn)
 | 
			
		||||
            { } catch { }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Burn any excess ETH to avoid balance issues for sources which use ETH directly
 | 
			
		||||
        address(0).transfer(address(this).balance);
 | 
			
		||||
 | 
			
		||||
        uint256[] memory amountsOut = new uint256[](amountsIn.length);
 | 
			
		||||
        uint256[] memory gasUsed = new uint256[](amountsIn.length);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < amountsIn.length; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                this._callRevert{gas: CALL_STIPEND}(
 | 
			
		||||
                    selector,
 | 
			
		||||
                    sellToken,
 | 
			
		||||
                    buyToken,
 | 
			
		||||
                    bridgeData,
 | 
			
		||||
                    amountsIn[i]
 | 
			
		||||
                )
 | 
			
		||||
            {
 | 
			
		||||
                require(false, "Swap Sample should have reverted");
 | 
			
		||||
            } catch (bytes memory reason) {
 | 
			
		||||
                // Parse the reverted sample data
 | 
			
		||||
                (amountsOut[i], gasUsed[i]) = _parseRevertedSingleSwapSample(reason);
 | 
			
		||||
                // If we detect the amount out is 0 then we return early
 | 
			
		||||
                // rather than continue performing excess work
 | 
			
		||||
 | 
			
		||||
                // Some sources (Balancer) display issues, especially with small amounts
 | 
			
		||||
                // Where the amountsOut can range, e.g 448,0,0,0,2476,3048,0,4279,4941,0,0,7133,
 | 
			
		||||
 | 
			
		||||
                if (amountsOut[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Revert the entire sampling
 | 
			
		||||
        _revertSwapSample(amountsOut, gasUsed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSwapQuotesRevert(
 | 
			
		||||
        SwapRevertSamplerQuoteOpts memory opts,
 | 
			
		||||
        uint256[] memory amountsIn
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory amountsOut)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
            this._mintCallRevert(
 | 
			
		||||
                opts.getSwapQuoteCallback.selector,
 | 
			
		||||
                opts.sellToken,
 | 
			
		||||
                opts.buyToken,
 | 
			
		||||
                opts.bridgeData,
 | 
			
		||||
                amountsIn
 | 
			
		||||
            )
 | 
			
		||||
        {
 | 
			
		||||
            require(false, "Swap Sample should have reverted");
 | 
			
		||||
        } catch (bytes memory reason) {
 | 
			
		||||
            // Parse the reverted sample datas
 | 
			
		||||
            (amountsOut, gasUsed) = abi.decode(reason, (uint256[], uint256[]));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getNativeWrappedToken()
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (IEtherTokenV06)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 chainId;
 | 
			
		||||
        assembly {
 | 
			
		||||
            chainId := chainid()
 | 
			
		||||
        }
 | 
			
		||||
        address token;
 | 
			
		||||
        if (chainId == 1) {
 | 
			
		||||
            // Ethereum Mainnet
 | 
			
		||||
            token = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
 | 
			
		||||
        } else if (chainId == 3) {
 | 
			
		||||
            // Ropsten
 | 
			
		||||
            token = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
 | 
			
		||||
        } else if (chainId == 4) {
 | 
			
		||||
            // Rinkeby
 | 
			
		||||
            token = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
 | 
			
		||||
        } else if (chainId == 42) {
 | 
			
		||||
            // Kovan
 | 
			
		||||
            token = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
 | 
			
		||||
        } else if (chainId == 56) {
 | 
			
		||||
            // BSC 
 | 
			
		||||
            token = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
 | 
			
		||||
        } else if (chainId == 137) {
 | 
			
		||||
            // Polygon
 | 
			
		||||
            token = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
 | 
			
		||||
        } else if (chainId == 1337) {
 | 
			
		||||
            // 0x Ganache
 | 
			
		||||
            token = 0x0B1ba0af832d7C05fD64161E0Db78E85978E8082;
 | 
			
		||||
        }
 | 
			
		||||
        if (token == address(0)) {
 | 
			
		||||
            revert("No native wrapped token");
 | 
			
		||||
        }
 | 
			
		||||
        return IEtherTokenV06(token);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _revertSingleSwapSample(
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        uint256 gasUsed
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
    {
 | 
			
		||||
        // Revert it so there is no state change
 | 
			
		||||
        assembly {
 | 
			
		||||
            mstore(0, amount)
 | 
			
		||||
            mstore(32, gasUsed)
 | 
			
		||||
            revert(0, 64)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _revertSwapSample(
 | 
			
		||||
        uint256[] memory amounts,
 | 
			
		||||
        uint256[] memory gasUsed
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
    {
 | 
			
		||||
        bytes memory data = abi.encode(amounts, gasUsed);
 | 
			
		||||
        // Revert it so there is no state change
 | 
			
		||||
        assembly {
 | 
			
		||||
            revert(add(data, 32), mload(data))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Parses the reverted swap sample data. If no amount
 | 
			
		||||
    ///      is decoded, 0 is returned.
 | 
			
		||||
    /// @param reason the string which contains the possible
 | 
			
		||||
    ///               sample amount
 | 
			
		||||
    /// @return the decoded sample amount or 0
 | 
			
		||||
    /// @return the gas used in the sample
 | 
			
		||||
    function _parseRevertedSingleSwapSample(
 | 
			
		||||
        bytes memory reason
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        pure
 | 
			
		||||
        returns (uint256, uint256)
 | 
			
		||||
    {
 | 
			
		||||
        if (reason.length != 64) {
 | 
			
		||||
            return (0,0);
 | 
			
		||||
        }
 | 
			
		||||
        return abi.decode(reason, (uint256, uint256));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSwapApproximateBuys(
 | 
			
		||||
        SwapRevertSamplerBuyQuoteOpts memory opts,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        if (makerTokenAmounts.length == 0) {
 | 
			
		||||
            return (gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = new uint256[](makerTokenAmounts.length);
 | 
			
		||||
        gasUsed = new uint256[](makerTokenAmounts.length);
 | 
			
		||||
 | 
			
		||||
        uint256[] memory sellAmounts = new uint256[](1);
 | 
			
		||||
        sellAmounts[0] = makerTokenAmounts[0];
 | 
			
		||||
 | 
			
		||||
        SwapRevertSamplerQuoteOpts memory sellOpts = SwapRevertSamplerQuoteOpts({
 | 
			
		||||
            sellToken: opts.sellToken,
 | 
			
		||||
            buyToken: opts.buyToken,
 | 
			
		||||
            bridgeData: opts.sellTokenData,
 | 
			
		||||
            getSwapQuoteCallback: opts.getSwapQuoteCallback
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        SwapRevertSamplerQuoteOpts memory buyOpts = SwapRevertSamplerQuoteOpts({
 | 
			
		||||
            sellToken: opts.buyToken,
 | 
			
		||||
            buyToken: opts.sellToken,
 | 
			
		||||
            bridgeData: opts.buyTokenData,
 | 
			
		||||
            getSwapQuoteCallback: opts.getSwapQuoteCallback
 | 
			
		||||
        });
 | 
			
		||||
        // Inverted, perform a sell of the token the user wants to buy
 | 
			
		||||
        (, sellAmounts) = _sampleSwapQuotesRevert(buyOpts, sellAmounts);
 | 
			
		||||
        if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
 | 
			
		||||
            return (gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint256[] memory buyAmounts;
 | 
			
		||||
        // Sell of the token the user wishes to dispose, see how much we buy
 | 
			
		||||
        (, buyAmounts) = _sampleSwapQuotesRevert(sellOpts, sellAmounts);
 | 
			
		||||
 | 
			
		||||
        if (buyAmounts.length == 0 || buyAmounts[0] == 0) {
 | 
			
		||||
            return (gasUsed, takerTokenAmounts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
 | 
			
		||||
            uint256[] memory _gasUsed;
 | 
			
		||||
            for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
 | 
			
		||||
                // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
 | 
			
		||||
                sellAmounts[0] = _safeGetPartialAmountCeil(
 | 
			
		||||
                    makerTokenAmounts[i],
 | 
			
		||||
                    buyAmounts[0],
 | 
			
		||||
                    sellAmounts[0]
 | 
			
		||||
                );
 | 
			
		||||
                if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                sellAmounts[0] = _safeGetPartialAmountCeil(
 | 
			
		||||
                    (ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
 | 
			
		||||
                    ONE_HUNDED_PERCENT_BPS,
 | 
			
		||||
                    sellAmounts[0]
 | 
			
		||||
                );
 | 
			
		||||
                if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                uint256[] memory _buyAmounts;
 | 
			
		||||
                (_gasUsed, _buyAmounts) = _sampleSwapQuotesRevert(sellOpts, sellAmounts);
 | 
			
		||||
                if (_buyAmounts.length == 0 || _buyAmounts[0] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // We re-use buyAmount next iteration, only assign if it is
 | 
			
		||||
                // non zero
 | 
			
		||||
                buyAmounts = _buyAmounts;
 | 
			
		||||
                // If we've reached our goal, exit early
 | 
			
		||||
                if (buyAmounts[0] >= makerTokenAmounts[i]) {
 | 
			
		||||
                    uint256 eps =
 | 
			
		||||
                        (buyAmounts[0] - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
 | 
			
		||||
                        makerTokenAmounts[i];
 | 
			
		||||
                    if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // We've encountered reverts, so bail
 | 
			
		||||
            if (_gasUsed.length == 0 || _gasUsed[0] == 0) {
 | 
			
		||||
                return (gasUsed, takerTokenAmounts);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (buyAmounts.length > 0) {
 | 
			
		||||
                gasUsed[i] = _gasUsed[0];
 | 
			
		||||
                // We do our best to close in on the requested amount, but we can either over buy or under buy and exit
 | 
			
		||||
                // if we hit a max iteration limit
 | 
			
		||||
                // We scale the sell amount to get the approximate target
 | 
			
		||||
                takerTokenAmounts[i] = _safeGetPartialAmountCeil(
 | 
			
		||||
                    makerTokenAmounts[i],
 | 
			
		||||
                    buyAmounts[0],
 | 
			
		||||
                    sellAmounts[0]
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _safeGetPartialAmountCeil(
 | 
			
		||||
        uint256 numerator,
 | 
			
		||||
        uint256 denominator,
 | 
			
		||||
        uint256 target
 | 
			
		||||
    )
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 partialAmount)
 | 
			
		||||
    {
 | 
			
		||||
        if (numerator == 0 || target == 0 || denominator == 0) return 0;
 | 
			
		||||
        uint256 c = numerator * target;
 | 
			
		||||
        if (c / numerator != target) return 0;
 | 
			
		||||
        return (c + (denominator - 1)) / denominator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -40,10 +40,11 @@ contract TwoHopSampler {
 | 
			
		||||
        returns (
 | 
			
		||||
            HopInfo memory firstHop,
 | 
			
		||||
            HopInfo memory secondHop,
 | 
			
		||||
            uint256 intermediateAssetAmount,
 | 
			
		||||
            uint256 buyAmount
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        uint256 intermediateAssetAmount = 0;
 | 
			
		||||
        intermediateAssetAmount = 0;
 | 
			
		||||
        for (uint256 i = 0; i != firstHopCalls.length; ++i) {
 | 
			
		||||
            firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, sellAmount);
 | 
			
		||||
            (bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]);
 | 
			
		||||
@@ -57,7 +58,7 @@ contract TwoHopSampler {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (intermediateAssetAmount == 0) {
 | 
			
		||||
            return (firstHop, secondHop, buyAmount);
 | 
			
		||||
            return (firstHop, secondHop, intermediateAssetAmount, buyAmount);
 | 
			
		||||
        }
 | 
			
		||||
        for (uint256 j = 0; j != secondHopCalls.length; ++j) {
 | 
			
		||||
            secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, intermediateAssetAmount);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -20,32 +20,43 @@
 | 
			
		||||
pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IUniswapExchangeQuotes.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IUniswapExchangeFactory {
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the exchange for a token.
 | 
			
		||||
    /// @param tokenAddress The address of the token contract.
 | 
			
		||||
    function getExchange(address tokenAddress)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswap.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
contract UniswapSampler is
 | 
			
		||||
    SamplerUtils
 | 
			
		||||
    MixinUniswap,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for Uniswap calls.
 | 
			
		||||
    uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    constructor(IEtherTokenV06 weth)
 | 
			
		||||
        public
 | 
			
		||||
        MixinUniswap(weth)
 | 
			
		||||
    { }
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromUniswap(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeUniswapInternal(
 | 
			
		||||
            _getNativeWrappedToken(),
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Uniswap.
 | 
			
		||||
    /// @param router Address of the Uniswap Router
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswap(
 | 
			
		||||
@@ -55,59 +66,24 @@ contract UniswapSampler is
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
 | 
			
		||||
        IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            bool didSucceed = true;
 | 
			
		||||
            if (makerToken == address(0)) {
 | 
			
		||||
                (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(takerTokenExchange),
 | 
			
		||||
                    takerTokenExchange.getTokenToEthInputPrice.selector,
 | 
			
		||||
                    takerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else if (takerToken == address(0)) {
 | 
			
		||||
                (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(makerTokenExchange),
 | 
			
		||||
                    makerTokenExchange.getEthToTokenInputPrice.selector,
 | 
			
		||||
                    takerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                uint256 ethBought;
 | 
			
		||||
                (ethBought, didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(takerTokenExchange),
 | 
			
		||||
                    takerTokenExchange.getTokenToEthInputPrice.selector,
 | 
			
		||||
                    takerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
                if (ethBought != 0) {
 | 
			
		||||
                    (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                        address(makerTokenExchange),
 | 
			
		||||
                        makerTokenExchange.getEthToTokenInputPrice.selector,
 | 
			
		||||
                        ethBought
 | 
			
		||||
                    );
 | 
			
		||||
                } else {
 | 
			
		||||
                    makerTokenAmounts[i] = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // Break early if amounts are 0
 | 
			
		||||
            if (!didSucceed || makerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                bridgeData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromUniswap
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Uniswap.
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param makerTokenAmounts Maker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswap(
 | 
			
		||||
@@ -117,98 +93,18 @@ contract UniswapSampler is
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
 | 
			
		||||
        IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
 | 
			
		||||
        IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            bool didSucceed = true;
 | 
			
		||||
            if (makerToken == address(0)) {
 | 
			
		||||
                (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(takerTokenExchange),
 | 
			
		||||
                    takerTokenExchange.getTokenToEthOutputPrice.selector,
 | 
			
		||||
                    makerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else if (takerToken == address(0)) {
 | 
			
		||||
                (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(makerTokenExchange),
 | 
			
		||||
                    makerTokenExchange.getEthToTokenOutputPrice.selector,
 | 
			
		||||
                    makerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else {
 | 
			
		||||
                uint256 ethSold;
 | 
			
		||||
                (ethSold, didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(makerTokenExchange),
 | 
			
		||||
                    makerTokenExchange.getEthToTokenOutputPrice.selector,
 | 
			
		||||
                    makerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
                if (ethSold != 0) {
 | 
			
		||||
                    (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                        address(takerTokenExchange),
 | 
			
		||||
                        takerTokenExchange.getTokenToEthOutputPrice.selector,
 | 
			
		||||
                        ethSold
 | 
			
		||||
                    );
 | 
			
		||||
                } else {
 | 
			
		||||
                    takerTokenAmounts[i] = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // Break early if amounts are 0
 | 
			
		||||
            if (!didSucceed || takerTokenAmounts[i] == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Gracefully calls a Uniswap pricing function.
 | 
			
		||||
    /// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
 | 
			
		||||
    /// @param functionSelector Selector of the target function.
 | 
			
		||||
    /// @param inputAmount Quantity parameter particular to the pricing function.
 | 
			
		||||
    /// @return outputAmount The returned amount from the function call. Will be
 | 
			
		||||
    ///         zero if the call fails or if `uniswapExchangeAddress` is zero.
 | 
			
		||||
    function _callUniswapExchangePriceFunction(
 | 
			
		||||
        address uniswapExchangeAddress,
 | 
			
		||||
        bytes4 functionSelector,
 | 
			
		||||
        uint256 inputAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 outputAmount, bool didSucceed)
 | 
			
		||||
    {
 | 
			
		||||
        if (uniswapExchangeAddress == address(0)) {
 | 
			
		||||
            return (outputAmount, didSucceed);
 | 
			
		||||
        }
 | 
			
		||||
        bytes memory resultData;
 | 
			
		||||
        (didSucceed, resultData) =
 | 
			
		||||
            uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
 | 
			
		||||
                abi.encodeWithSelector(
 | 
			
		||||
                    functionSelector,
 | 
			
		||||
                    inputAmount
 | 
			
		||||
                ));
 | 
			
		||||
        if (didSucceed) {
 | 
			
		||||
            outputAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Retrive an existing Uniswap exchange contract.
 | 
			
		||||
    ///      Throws if the exchange does not exist.
 | 
			
		||||
    /// @param router Address of the Uniswap router.
 | 
			
		||||
    /// @param tokenAddress Address of the token contract.
 | 
			
		||||
    /// @return exchange `IUniswapExchangeQuotes` for the token.
 | 
			
		||||
    function _getUniswapExchange(address router, address tokenAddress)
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (IUniswapExchangeQuotes exchange)
 | 
			
		||||
    {
 | 
			
		||||
        exchange = IUniswapExchangeQuotes(
 | 
			
		||||
            address(IUniswapExchangeFactory(router)
 | 
			
		||||
            .getExchange(tokenAddress))
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: takerToken,
 | 
			
		||||
                buyToken: makerToken,
 | 
			
		||||
                sellTokenData: abi.encode(router),
 | 
			
		||||
                buyTokenData: abi.encode(router),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromUniswap
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 ZeroEx Intl.
 | 
			
		||||
  Copyright 2021 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
@@ -21,17 +21,36 @@ pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./interfaces/IUniswapV2Router01.sol";
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract UniswapV2Sampler
 | 
			
		||||
contract UniswapV2Sampler is
 | 
			
		||||
    MixinUniswapV2,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for UniswapV2 calls.
 | 
			
		||||
    uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromUniswapV2(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeUniswapV2(
 | 
			
		||||
            IERC20TokenV06(buyToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from UniswapV2.
 | 
			
		||||
    /// @param router Router to look up tokens and amounts
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed for each sample amount
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswapV2(
 | 
			
		||||
@@ -40,34 +59,24 @@ contract UniswapV2Sampler
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IUniswapV2Router01(router).getAmountsOut
 | 
			
		||||
                    {gas: UNISWAPV2_CALL_GAS}
 | 
			
		||||
                    (takerTokenAmounts[i], path)
 | 
			
		||||
                returns (uint256[] memory amounts)
 | 
			
		||||
            {
 | 
			
		||||
                makerTokenAmounts[i] = amounts[path.length - 1];
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (makerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
 | 
			
		||||
            SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                sellToken: path[0],
 | 
			
		||||
                buyToken: path[path.length - 1],
 | 
			
		||||
                bridgeData: abi.encode(router, path),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromUniswapV2
 | 
			
		||||
            }),
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from UniswapV2.
 | 
			
		||||
    /// @param router Router to look up tokens and amounts
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed for each sample amount
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswapV2(
 | 
			
		||||
@@ -76,27 +85,22 @@ contract UniswapV2Sampler
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
        returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            try
 | 
			
		||||
                IUniswapV2Router01(router).getAmountsIn
 | 
			
		||||
                    {gas: UNISWAPV2_CALL_GAS}
 | 
			
		||||
                    (makerTokenAmounts[i], path)
 | 
			
		||||
                returns (uint256[] memory amounts)
 | 
			
		||||
            {
 | 
			
		||||
                takerTokenAmounts[i] = amounts[0];
 | 
			
		||||
                // Break early if there are 0 amounts
 | 
			
		||||
                if (takerTokenAmounts[i] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } catch (bytes memory) {
 | 
			
		||||
                // Swallow failures, leaving all results as zero.
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        address[] memory reversedPath = new address[](path.length);
 | 
			
		||||
        for (uint256 i = 0; i < path.length; ++i) {
 | 
			
		||||
            reversedPath[i] = path[path.length - i - 1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
 | 
			
		||||
            SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                sellToken: path[0],
 | 
			
		||||
                buyToken: path[path.length - 1],
 | 
			
		||||
                sellTokenData: abi.encode(router, path),
 | 
			
		||||
                buyTokenData: abi.encode(router, reversedPath),
 | 
			
		||||
                getSwapQuoteCallback: this.sampleSwapFromUniswapV2
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,18 +21,16 @@ pragma solidity ^0.6;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV3.sol";
 | 
			
		||||
import "./SwapRevertSampler.sol";
 | 
			
		||||
 | 
			
		||||
interface IUniswapV3Quoter {
 | 
			
		||||
    function factory()
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (IUniswapV3Factory factory);
 | 
			
		||||
    function quoteExactInput(bytes memory path, uint256 amountIn)
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256 amountOut);
 | 
			
		||||
    function quoteExactOutput(bytes memory path, uint256 amountOut)
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256 amountIn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IUniswapV3Factory {
 | 
			
		||||
@@ -48,26 +46,47 @@ interface IUniswapV3Pool {
 | 
			
		||||
    function fee() external view returns (uint24);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
contract UniswapV3Sampler
 | 
			
		||||
contract UniswapV3Sampler is
 | 
			
		||||
    MixinUniswapV3,
 | 
			
		||||
    SwapRevertSampler
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
 | 
			
		||||
    uint256 constant private QUOTE_GAS = 300e3;
 | 
			
		||||
    using LibRichErrorsV06 for bytes;
 | 
			
		||||
 | 
			
		||||
    function sampleSwapFromUniswapV3(
 | 
			
		||||
        address sellToken,
 | 
			
		||||
        address buyToken,
 | 
			
		||||
        bytes memory bridgeData,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return _tradeUniswapV3(
 | 
			
		||||
            IERC20TokenV06(sellToken),
 | 
			
		||||
            takerTokenAmount,
 | 
			
		||||
            bridgeData
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from UniswapV3.
 | 
			
		||||
    /// @param quoter UniswapV3 Quoter contract.
 | 
			
		||||
    /// @param router UniswapV3 Router contract.
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return uniswapPaths The encoded uniswap path for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswapV3(
 | 
			
		||||
        IUniswapV3Quoter quoter,
 | 
			
		||||
        address router,
 | 
			
		||||
        IERC20TokenV06[] memory path,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (
 | 
			
		||||
            bytes[] memory uniswapPaths,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory makerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
@@ -75,50 +94,57 @@ contract UniswapV3Sampler
 | 
			
		||||
            _getValidPoolPaths(quoter.factory(), path, 0);
 | 
			
		||||
 | 
			
		||||
        makerTokenAmounts = new uint256[](takerTokenAmounts.length);
 | 
			
		||||
        gasUsed = new uint256[](takerTokenAmounts.length);
 | 
			
		||||
        uniswapPaths = new bytes[](takerTokenAmounts.length);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < takerTokenAmounts.length; ++i) {
 | 
			
		||||
            // Pick the best result from all the paths.
 | 
			
		||||
            bytes memory topUniswapPath;
 | 
			
		||||
            uint256 topBuyAmount = 0;
 | 
			
		||||
            for (uint256 j = 0; j < poolPaths.length; ++j) {
 | 
			
		||||
                bytes memory uniswapPath = _toUniswapPath(path, poolPaths[j]);
 | 
			
		||||
                try
 | 
			
		||||
                    quoter.quoteExactInput
 | 
			
		||||
                        { gas: QUOTE_GAS }
 | 
			
		||||
                        (uniswapPath, takerTokenAmounts[i])
 | 
			
		||||
                        returns (uint256 buyAmount)
 | 
			
		||||
                {
 | 
			
		||||
                    if (topBuyAmount <= buyAmount) {
 | 
			
		||||
                        topBuyAmount = buyAmount;
 | 
			
		||||
                        topUniswapPath = uniswapPath;
 | 
			
		||||
                    }
 | 
			
		||||
                } catch { }
 | 
			
		||||
        for (uint256 i = 0; i < poolPaths.length; ++i) {
 | 
			
		||||
            bytes memory _uniswapPath = _toUniswapPath(path, poolPaths[i]);
 | 
			
		||||
            (
 | 
			
		||||
                uint256[] memory _gasUsed,
 | 
			
		||||
                uint256[] memory _makerTokenAmounts
 | 
			
		||||
            ) = _sampleSwapQuotesRevert(
 | 
			
		||||
                SwapRevertSamplerQuoteOpts({
 | 
			
		||||
                    sellToken: address(path[0]),
 | 
			
		||||
                    buyToken: address(path[path.length - 1]),
 | 
			
		||||
                    bridgeData: abi.encode(router, _uniswapPath),
 | 
			
		||||
                    getSwapQuoteCallback: this.sampleSwapFromUniswapV3
 | 
			
		||||
                }),
 | 
			
		||||
                takerTokenAmounts
 | 
			
		||||
            );
 | 
			
		||||
            for (uint256 j = 0; j < _makerTokenAmounts.length; ++j) {
 | 
			
		||||
                // Break early if we can't complete the sells.
 | 
			
		||||
                if (_makerTokenAmounts[j] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // If this is better than what we have found, prefer it
 | 
			
		||||
                if (makerTokenAmounts[j] <= _makerTokenAmounts[j]) {
 | 
			
		||||
                    makerTokenAmounts[j] = _makerTokenAmounts[j];
 | 
			
		||||
                    gasUsed[j] = _gasUsed[j];
 | 
			
		||||
                    uniswapPaths[j] = _uniswapPath;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // Break early if we can't complete the buys.
 | 
			
		||||
            if (topBuyAmount == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = topBuyAmount;
 | 
			
		||||
            uniswapPaths[i] = topUniswapPath;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from UniswapV3.
 | 
			
		||||
    /// @param quoter UniswapV3 Quoter contract.
 | 
			
		||||
    /// @param router UniswapV3 Router contract.
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return uniswapPaths The encoded uniswap path for each sample.
 | 
			
		||||
    /// @return gasUsed gas consumed in each sample sell
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswapV3(
 | 
			
		||||
        IUniswapV3Quoter quoter,
 | 
			
		||||
        address router,
 | 
			
		||||
        IERC20TokenV06[] memory path,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        returns (
 | 
			
		||||
            bytes[] memory uniswapPaths,
 | 
			
		||||
            uint256[] memory gasUsed,
 | 
			
		||||
            uint256[] memory takerTokenAmounts
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
@@ -127,37 +153,43 @@ contract UniswapV3Sampler
 | 
			
		||||
        IERC20TokenV06[] memory reversedPath = _reverseTokenPath(path);
 | 
			
		||||
 | 
			
		||||
        takerTokenAmounts = new uint256[](makerTokenAmounts.length);
 | 
			
		||||
        gasUsed = new uint256[](makerTokenAmounts.length);
 | 
			
		||||
        uniswapPaths = new bytes[](makerTokenAmounts.length);
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < makerTokenAmounts.length; ++i) {
 | 
			
		||||
            // Pick the best result from all the paths.
 | 
			
		||||
            bytes memory topUniswapPath;
 | 
			
		||||
            uint256 topSellAmount = 0;
 | 
			
		||||
            for (uint256 j = 0; j < poolPaths.length; ++j) {
 | 
			
		||||
                // quoter requires path to be reversed for buys.
 | 
			
		||||
                bytes memory uniswapPath = _toUniswapPath(
 | 
			
		||||
                    reversedPath,
 | 
			
		||||
                    _reversePoolPath(poolPaths[j])
 | 
			
		||||
                );
 | 
			
		||||
                try
 | 
			
		||||
                    quoter.quoteExactOutput
 | 
			
		||||
                        { gas: QUOTE_GAS }
 | 
			
		||||
                        (uniswapPath, makerTokenAmounts[i])
 | 
			
		||||
                        returns (uint256 sellAmount)
 | 
			
		||||
                {
 | 
			
		||||
                    if (topSellAmount == 0 || topSellAmount >= sellAmount) {
 | 
			
		||||
                        topSellAmount = sellAmount;
 | 
			
		||||
                        // But the output path should still be encoded for sells.
 | 
			
		||||
                        topUniswapPath = _toUniswapPath(path, poolPaths[j]);
 | 
			
		||||
                    }
 | 
			
		||||
                } catch {}
 | 
			
		||||
        for (uint256 i = 0; i < poolPaths.length; ++i) {
 | 
			
		||||
            (
 | 
			
		||||
                uint256[] memory _gasUsed,
 | 
			
		||||
                uint256[] memory _takerTokenAmounts
 | 
			
		||||
            ) = _sampleSwapApproximateBuys(
 | 
			
		||||
                SwapRevertSamplerBuyQuoteOpts({
 | 
			
		||||
                    sellToken: address(path[0]),
 | 
			
		||||
                    buyToken: address(path[path.length - 1]),
 | 
			
		||||
                    sellTokenData: abi.encode(router, _toUniswapPath(path, poolPaths[i])),
 | 
			
		||||
                    buyTokenData: abi.encode(
 | 
			
		||||
                        router,
 | 
			
		||||
                        _toUniswapPath(
 | 
			
		||||
                            reversedPath,
 | 
			
		||||
                            _reversePoolPath(poolPaths[i])
 | 
			
		||||
                        )
 | 
			
		||||
                    ),
 | 
			
		||||
                    getSwapQuoteCallback: this.sampleSwapFromUniswapV3
 | 
			
		||||
                }),
 | 
			
		||||
                makerTokenAmounts
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            for (uint256 j = 0; j < _takerTokenAmounts.length; ++j) {
 | 
			
		||||
                // Break early if we can't complete the buys.
 | 
			
		||||
                if (_takerTokenAmounts[j] == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // We can go from high to low here
 | 
			
		||||
                if (takerTokenAmounts[j] == 0 || takerTokenAmounts[j] >= _takerTokenAmounts[j]) {
 | 
			
		||||
                    takerTokenAmounts[j] = _takerTokenAmounts[j];
 | 
			
		||||
                    gasUsed[j] = _gasUsed[j];
 | 
			
		||||
                    // But the output path should still be encoded for sells.
 | 
			
		||||
                    uniswapPaths[j] = _toUniswapPath(path, poolPaths[i]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // Break early if we can't complete the buys.
 | 
			
		||||
            if (topSellAmount == 0) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            takerTokenAmounts[i] = topSellAmount;
 | 
			
		||||
            uniswapPaths[i] = topUniswapPath;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -268,7 +300,7 @@ contract UniswapV3Sampler
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Must have a balance of both tokens.
 | 
			
		||||
        // // Must have a balance of both tokens.
 | 
			
		||||
        if (pool.token0().balanceOf(address(pool)) == 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -77,4 +77,24 @@ contract UtilitySampler {
 | 
			
		||||
        assembly { size := extcodesize(account) }
 | 
			
		||||
        return size > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getCode(address addr)
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes memory code)
 | 
			
		||||
    {
 | 
			
		||||
        assembly {
 | 
			
		||||
            // retrieve the size of the code, this needs assembly
 | 
			
		||||
            let size := extcodesize(addr)
 | 
			
		||||
            // allocate output byte array - this could also be done without assembly
 | 
			
		||||
            // by using o_code = new bytes(size)
 | 
			
		||||
            code := mload(0x40)
 | 
			
		||||
            // new "memory end" including padding
 | 
			
		||||
            mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
 | 
			
		||||
            // store length in memory
 | 
			
		||||
            mstore(code, size)
 | 
			
		||||
            // actually retrieve the code, this needs assembly
 | 
			
		||||
            extcodecopy(addr, add(code, 0x20), 0, size)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IBalancer {
 | 
			
		||||
    function isBound(address t) external view returns (bool);
 | 
			
		||||
    function getDenormalizedWeight(address token) external view returns (uint256);
 | 
			
		||||
    function getBalance(address token) external view returns (uint256);
 | 
			
		||||
    function getSwapFee() external view returns (uint256);
 | 
			
		||||
    function calcOutGivenIn(
 | 
			
		||||
        uint256 tokenBalanceIn,
 | 
			
		||||
        uint256 tokenWeightIn,
 | 
			
		||||
        uint256 tokenBalanceOut,
 | 
			
		||||
        uint256 tokenWeightOut,
 | 
			
		||||
        uint256 tokenAmountIn,
 | 
			
		||||
        uint256 swapFee
 | 
			
		||||
    ) external pure returns (uint256 tokenAmountOut);
 | 
			
		||||
    function calcInGivenOut(
 | 
			
		||||
        uint256 tokenBalanceIn,
 | 
			
		||||
        uint256 tokenWeightIn,
 | 
			
		||||
        uint256 tokenBalanceOut,
 | 
			
		||||
        uint256 tokenWeightOut,
 | 
			
		||||
        uint256 tokenAmountOut,
 | 
			
		||||
        uint256 swapFee
 | 
			
		||||
    ) external pure returns (uint256 tokenAmountIn);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IBancor {}
 | 
			
		||||
 | 
			
		||||
interface IBancorNetwork {
 | 
			
		||||
  function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory);
 | 
			
		||||
  function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IBancorRegistry {
 | 
			
		||||
    function getAddress(bytes32 _contractName) external view returns (address);
 | 
			
		||||
    function BANCOR_NETWORK() external view returns (bytes32);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// solhint-disable func-name-mixedcase
 | 
			
		||||
interface ICurve {
 | 
			
		||||
 | 
			
		||||
    /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
 | 
			
		||||
    ///      This function exists on later versions of Curve (USDC/DAI/USDT)
 | 
			
		||||
    /// @param i The token index being sold.
 | 
			
		||||
    /// @param j The token index being bought.
 | 
			
		||||
    /// @param sellAmount The amount of token being bought.
 | 
			
		||||
    /// @param minBuyAmount The minimum buy amount of the token being bought.
 | 
			
		||||
    function exchange_underlying(
 | 
			
		||||
        int128 i,
 | 
			
		||||
        int128 j,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        uint256 minBuyAmount
 | 
			
		||||
    )
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
 | 
			
		||||
    /// @param i The token index being sold.
 | 
			
		||||
    /// @param j The token index being bought.
 | 
			
		||||
    /// @param sellAmount The amount of token being bought.
 | 
			
		||||
    function get_dy_underlying(
 | 
			
		||||
        int128 i,
 | 
			
		||||
        int128 j,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256 dy);
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
 | 
			
		||||
    ///      This function exists on later versions of Curve (USDC/DAI/USDT)
 | 
			
		||||
    /// @param i The token index being sold.
 | 
			
		||||
    /// @param j The token index being bought.
 | 
			
		||||
    /// @param buyAmount The amount of token being bought.
 | 
			
		||||
    function get_dx_underlying(
 | 
			
		||||
        int128 i,
 | 
			
		||||
        int128 j,
 | 
			
		||||
        uint256 buyAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256 dx);
 | 
			
		||||
 | 
			
		||||
    /// @dev Get the underlying token address from the token index
 | 
			
		||||
    /// @param i The token index.
 | 
			
		||||
    function underlying_coins(
 | 
			
		||||
        int128 i
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (address tokenAddress);
 | 
			
		||||
}
 | 
			
		||||
@@ -22,22 +22,6 @@ pragma solidity ^0.6;
 | 
			
		||||
// Keepin everything together
 | 
			
		||||
interface IKyberNetwork {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IKyberNetworkProxy {
 | 
			
		||||
 | 
			
		||||
    function getExpectedRateAfterFee(
 | 
			
		||||
        address src,
 | 
			
		||||
        address dest,
 | 
			
		||||
        uint256 srcQty,
 | 
			
		||||
        uint256 platformFeeBps,
 | 
			
		||||
        bytes calldata hint
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 expectedRate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IKyberHintHandler {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IMStable {
 | 
			
		||||
 | 
			
		||||
    function getSwapOutput(
 | 
			
		||||
        address _input,
 | 
			
		||||
        address _output,
 | 
			
		||||
        uint256 _quantity
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 swapOutput);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IMooniswapRegistry {
 | 
			
		||||
 | 
			
		||||
    function pools(address token1, address token2) external view returns(address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface IMooniswap {
 | 
			
		||||
 | 
			
		||||
    function getReturn(
 | 
			
		||||
        address fromToken,
 | 
			
		||||
        address destToken,
 | 
			
		||||
        uint256 amount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns(uint256 returnAmount);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IMultiBridge {
 | 
			
		||||
 | 
			
		||||
    /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
 | 
			
		||||
    /// @param tokenAddress The address of the ERC20 token to transfer.
 | 
			
		||||
    /// @param from Address to transfer asset from.
 | 
			
		||||
    /// @param to Address to transfer asset to.
 | 
			
		||||
    /// @param amount Amount of asset to transfer.
 | 
			
		||||
    /// @param bridgeData Arbitrary asset data needed by the bridge contract.
 | 
			
		||||
    /// @return success The magic bytes `0xdc1600f3` if successful.
 | 
			
		||||
    function bridgeTransferFrom(
 | 
			
		||||
        address tokenAddress,
 | 
			
		||||
        address from,
 | 
			
		||||
        address to,
 | 
			
		||||
        uint256 amount,
 | 
			
		||||
        bytes calldata bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        returns (bytes4 success);
 | 
			
		||||
 | 
			
		||||
    /// @dev Quotes the amount of `makerToken` that would be obtained by
 | 
			
		||||
    ///      selling `sellAmount` of `takerToken`.
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param intermediateToken The address of the intermediate token to
 | 
			
		||||
    ///        use in an indirect route.
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param sellAmount Amount of `takerToken` to sell.
 | 
			
		||||
    /// @return makerTokenAmount Amount of `makerToken` that would be obtained.
 | 
			
		||||
    function getSellQuote(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address intermediateToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 makerTokenAmount);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2020 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IShell {
 | 
			
		||||
 | 
			
		||||
    function viewOriginSwap (
 | 
			
		||||
        address from,
 | 
			
		||||
        address to,
 | 
			
		||||
        uint256 fromAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 toAmount);
 | 
			
		||||
 | 
			
		||||
    function viewTargetSwap (
 | 
			
		||||
        address from,
 | 
			
		||||
        address to,
 | 
			
		||||
        uint256 toAmount
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 fromAmount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,45 +0,0 @@
 | 
			
		||||
// SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2021 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.6;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface ISmoothy {
 | 
			
		||||
 | 
			
		||||
    function getBalance (
 | 
			
		||||
        uint256 tid
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 balance);
 | 
			
		||||
 | 
			
		||||
    function _yBalances (
 | 
			
		||||
        uint256 tid
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 balance);
 | 
			
		||||
 | 
			
		||||
    function getTokenStats (
 | 
			
		||||
        uint256 tid
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 softWeight, uint256 hardWeight, uint256 balance, uint256 decimals);
 | 
			
		||||
}
 | 
			
		||||
@@ -127,7 +127,6 @@ contract FailTrigger {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestERC20BridgeSamplerUniswapExchange is
 | 
			
		||||
    IUniswapExchangeQuotes,
 | 
			
		||||
    TestDeploymentConstants,
 | 
			
		||||
    FailTrigger
 | 
			
		||||
{
 | 
			
		||||
@@ -145,7 +144,6 @@ contract TestERC20BridgeSamplerUniswapExchange is
 | 
			
		||||
    function getEthToTokenInputPrice(
 | 
			
		||||
        uint256 ethSold
 | 
			
		||||
    )
 | 
			
		||||
        override
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 tokensBought)
 | 
			
		||||
@@ -163,7 +161,6 @@ contract TestERC20BridgeSamplerUniswapExchange is
 | 
			
		||||
    function getEthToTokenOutputPrice(
 | 
			
		||||
        uint256 tokensBought
 | 
			
		||||
    )
 | 
			
		||||
        override
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 ethSold)
 | 
			
		||||
@@ -181,7 +178,6 @@ contract TestERC20BridgeSamplerUniswapExchange is
 | 
			
		||||
    function getTokenToEthInputPrice(
 | 
			
		||||
        uint256 tokensSold
 | 
			
		||||
    )
 | 
			
		||||
        override
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 ethBought)
 | 
			
		||||
@@ -199,7 +195,6 @@ contract TestERC20BridgeSamplerUniswapExchange is
 | 
			
		||||
    function getTokenToEthOutputPrice(
 | 
			
		||||
        uint256 ethBought
 | 
			
		||||
    )
 | 
			
		||||
        override
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 tokensSold)
 | 
			
		||||
@@ -376,6 +371,31 @@ contract TestERC20BridgeSamplerKyberNetwork is
 | 
			
		||||
            toToken
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function tradeWithHint(
 | 
			
		||||
        address fromToken,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        address toToken,
 | 
			
		||||
        address payable recipientAddress,
 | 
			
		||||
        uint256 maxBuyTokenAmount,
 | 
			
		||||
        uint256 minConversionRate,
 | 
			
		||||
        address payable walletId,
 | 
			
		||||
        bytes calldata hint
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        returns (uint256 boughtAmount)
 | 
			
		||||
    {
 | 
			
		||||
        _revertIfShouldFail();
 | 
			
		||||
        fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
 | 
			
		||||
        toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
 | 
			
		||||
        return LibDeterministicQuotes.getDeterministicSellQuote(
 | 
			
		||||
            SALT,
 | 
			
		||||
            fromToken,
 | 
			
		||||
            toToken,
 | 
			
		||||
            sellAmount
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -427,47 +447,17 @@ contract TestERC20BridgeSamplerEth2Dai is
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestERC20BridgeSamplerUniswapExchangeFactory is
 | 
			
		||||
    IUniswapExchangeFactory
 | 
			
		||||
{
 | 
			
		||||
    mapping (address => IUniswapExchangeQuotes) private _exchangesByToken;
 | 
			
		||||
 | 
			
		||||
    // Creates Uniswap exchange contracts for tokens.
 | 
			
		||||
    function createTokenExchanges(address[] calldata tokenAddresses)
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        for (uint256 i = 0; i < tokenAddresses.length; i++) {
 | 
			
		||||
            address tokenAddress = tokenAddresses[i];
 | 
			
		||||
            _exchangesByToken[tokenAddress] =
 | 
			
		||||
                new TestERC20BridgeSamplerUniswapExchange(tokenAddress);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // `IUniswapExchangeFactory.getExchange()`.
 | 
			
		||||
    function getExchange(address tokenAddress)
 | 
			
		||||
        override
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address)
 | 
			
		||||
    {
 | 
			
		||||
        return address(_exchangesByToken[tokenAddress]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract TestERC20BridgeSampler is
 | 
			
		||||
    ERC20BridgeSampler,
 | 
			
		||||
    FailTrigger
 | 
			
		||||
{
 | 
			
		||||
    TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
 | 
			
		||||
    TestERC20BridgeSamplerUniswapV2Router01 public uniswapV2Router;
 | 
			
		||||
    TestERC20BridgeSamplerEth2Dai public eth2Dai;
 | 
			
		||||
    TestERC20BridgeSamplerKyberNetwork public kyber;
 | 
			
		||||
 | 
			
		||||
    uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1;
 | 
			
		||||
 | 
			
		||||
    constructor() public ERC20BridgeSampler() {
 | 
			
		||||
        uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
 | 
			
		||||
    constructor() public ERC20BridgeSampler(IEtherTokenV06(address(0))) {
 | 
			
		||||
        uniswapV2Router = new TestERC20BridgeSamplerUniswapV2Router01();
 | 
			
		||||
        eth2Dai = new TestERC20BridgeSamplerEth2Dai();
 | 
			
		||||
        kyber = new TestERC20BridgeSamplerKyberNetwork();
 | 
			
		||||
@@ -477,7 +467,6 @@ contract TestERC20BridgeSampler is
 | 
			
		||||
    function createTokenExchanges(address[] calldata tokenAddresses)
 | 
			
		||||
        external
 | 
			
		||||
    {
 | 
			
		||||
        uniswap.createTokenExchanges(tokenAddresses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Overridden to return deterministic states.
 | 
			
		||||
@@ -493,14 +482,4 @@ contract TestERC20BridgeSampler is
 | 
			
		||||
    {
 | 
			
		||||
        return uint256(keccak256(abi.encode(order.salt))) % order.takerAmount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Overriden to return deterministic decimals.
 | 
			
		||||
    function _getTokenDecimals(address tokenAddress)
 | 
			
		||||
        override
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint8 decimals)
 | 
			
		||||
    {
 | 
			
		||||
        return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "@0x/asset-swapper",
 | 
			
		||||
    "version": "6.18.0",
 | 
			
		||||
    "version": "6.18.2",
 | 
			
		||||
    "engines": {
 | 
			
		||||
        "node": ">=6.12"
 | 
			
		||||
    },
 | 
			
		||||
@@ -37,9 +37,9 @@
 | 
			
		||||
        "sampler-size": "jq .compilerOutput.evm.deployedBytecode.object  -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))"
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
 | 
			
		||||
        "publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,DelegateHackedERC20,FakeTaker,HackedERC20,GasOverhead",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CurveSampler|CurveV2Sampler|DODOSampler|DODOV2Sampler|DelegateHackedERC20|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|GasOverhead|HackedERC20|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|ShellSampler|SwapRevertSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
 | 
			
		||||
        "postpublish": {
 | 
			
		||||
            "assets": []
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,16 @@
 | 
			
		||||
import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
import * as BalanceChecker from '../generated-artifacts/BalanceChecker.json';
 | 
			
		||||
import * as DelegateHackedERC20 from '../generated-artifacts/DelegateHackedERC20.json';
 | 
			
		||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
 | 
			
		||||
import * as FakeTaker from '../generated-artifacts/FakeTaker.json';
 | 
			
		||||
import * as GasOverhead from '../generated-artifacts/GasOverhead.json';
 | 
			
		||||
import * as HackedERC20 from '../generated-artifacts/HackedERC20.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    BalanceChecker: BalanceChecker as ContractArtifact,
 | 
			
		||||
    DelegateHackedERC20: DelegateHackedERC20 as ContractArtifact,
 | 
			
		||||
    FakeTaker: FakeTaker as ContractArtifact,
 | 
			
		||||
    HackedERC20: HackedERC20 as ContractArtifact,
 | 
			
		||||
    GasOverhead: GasOverhead as ContractArtifact,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -47,7 +47,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
 | 
			
		||||
    chainId: ChainId.Mainnet,
 | 
			
		||||
    orderRefreshIntervalMs: 10000, // 10 seconds
 | 
			
		||||
    ...DEFAULT_ORDER_PRUNER_OPTS,
 | 
			
		||||
    samplerGasLimit: 500e6,
 | 
			
		||||
    samplerGasLimit: 500e7,
 | 
			
		||||
    ethGasStationUrl: ETH_GAS_STATION_API_URL,
 | 
			
		||||
    rfqt: {
 | 
			
		||||
        takerApiKeyWhitelist: [],
 | 
			
		||||
@@ -91,8 +91,6 @@ export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) =>
 | 
			
		||||
const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
 | 
			
		||||
export const INVALID_SIGNATURE = { signatureType: SignatureType.Invalid, v: 1, r: EMPTY_BYTES32, s: EMPTY_BYTES32 };
 | 
			
		||||
 | 
			
		||||
export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_operation_utils/constants';
 | 
			
		||||
 | 
			
		||||
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
 | 
			
		||||
 | 
			
		||||
// tslint:disable-next-line: custom-no-magic-numbers
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,6 @@ export {
 | 
			
		||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
 | 
			
		||||
export {
 | 
			
		||||
    DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
 | 
			
		||||
    DEFAULT_GAS_SCHEDULE,
 | 
			
		||||
    SOURCE_FLAGS,
 | 
			
		||||
    BUY_SOURCE_FILTER_BY_CHAIN_ID,
 | 
			
		||||
    SELL_SOURCE_FILTER_BY_CHAIN_ID,
 | 
			
		||||
@@ -138,7 +137,6 @@ export {
 | 
			
		||||
    DODOFillData,
 | 
			
		||||
    ERC20BridgeSource,
 | 
			
		||||
    ExchangeProxyOverhead,
 | 
			
		||||
    FeeSchedule,
 | 
			
		||||
    Fill,
 | 
			
		||||
    FillData,
 | 
			
		||||
    GetMarketOrdersRfqOpts,
 | 
			
		||||
 
 | 
			
		||||
@@ -27,13 +27,16 @@ import {
 | 
			
		||||
import { assert } from './utils/assert';
 | 
			
		||||
import { MarketOperationUtils } from './utils/market_operation_utils';
 | 
			
		||||
import { BancorService } from './utils/market_operation_utils/bancor_service';
 | 
			
		||||
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
 | 
			
		||||
import {
 | 
			
		||||
    MAX_UINT256,
 | 
			
		||||
    NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
    SOURCE_FLAGS,
 | 
			
		||||
    ZERO_AMOUNT,
 | 
			
		||||
} from './utils/market_operation_utils/constants';
 | 
			
		||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
 | 
			
		||||
import { SourceFilters } from './utils/market_operation_utils/source_filters';
 | 
			
		||||
import {
 | 
			
		||||
    ERC20BridgeSource,
 | 
			
		||||
    FeeSchedule,
 | 
			
		||||
    FillData,
 | 
			
		||||
    GetMarketOrdersOpts,
 | 
			
		||||
    MarketDepth,
 | 
			
		||||
    MarketDepthSide,
 | 
			
		||||
@@ -115,10 +118,13 @@ export class SwapQuoter {
 | 
			
		||||
        // Allow the sampler bytecode to be overwritten using geths override functionality
 | 
			
		||||
        const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
 | 
			
		||||
        // Allow address of the Sampler to be overridden, i.e in Ganache where overrides do not work
 | 
			
		||||
        const samplerAddress = (options.samplerOverrides && options.samplerOverrides.to) || SAMPLER_ADDRESS;
 | 
			
		||||
        // We default the sampler address to be FlashWallet to account for allowances being set on tokens
 | 
			
		||||
        const samplerAddress =
 | 
			
		||||
            (options.samplerOverrides && options.samplerOverrides.to) ||
 | 
			
		||||
            this._contractAddresses.exchangeProxyFlashWallet;
 | 
			
		||||
        const defaultCodeOverrides = samplerBytecode
 | 
			
		||||
            ? {
 | 
			
		||||
                  [samplerAddress]: { code: samplerBytecode },
 | 
			
		||||
                  [samplerAddress]: { code: samplerBytecode, balance: MAX_UINT256 },
 | 
			
		||||
              }
 | 
			
		||||
            : {};
 | 
			
		||||
        const samplerOverrides = _.assign(
 | 
			
		||||
@@ -198,6 +204,7 @@ export class SwapQuoter {
 | 
			
		||||
        const optimizerResults = await this._marketOperationUtils.getBatchMarketBuyOrdersAsync(
 | 
			
		||||
            allOrders,
 | 
			
		||||
            makerTokenBuyAmounts,
 | 
			
		||||
            gasPrice,
 | 
			
		||||
            opts as GetMarketOrdersOpts,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +219,6 @@ export class SwapQuoter {
 | 
			
		||||
                        MarketOperation.Buy,
 | 
			
		||||
                        makerTokenBuyAmounts[i],
 | 
			
		||||
                        gasPrice,
 | 
			
		||||
                        opts.gasSchedule,
 | 
			
		||||
                        opts.bridgeSlippage,
 | 
			
		||||
                    );
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -268,6 +274,7 @@ export class SwapQuoter {
 | 
			
		||||
                        output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
 | 
			
		||||
                        fillData: o,
 | 
			
		||||
                        source: ERC20BridgeSource.Native,
 | 
			
		||||
                        gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
                    };
 | 
			
		||||
                }),
 | 
			
		||||
            ];
 | 
			
		||||
@@ -359,10 +366,8 @@ export class SwapQuoter {
 | 
			
		||||
        const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;
 | 
			
		||||
        const calcOpts: GetMarketOrdersOpts = {
 | 
			
		||||
            ...cloneOpts,
 | 
			
		||||
            feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData: FillData) =>
 | 
			
		||||
                gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)),
 | 
			
		||||
            ),
 | 
			
		||||
            exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
 | 
			
		||||
            gasPrice,
 | 
			
		||||
        };
 | 
			
		||||
        // pass the QuoteRequestor on if rfqt enabled
 | 
			
		||||
        if (calcOpts.rfqt !== undefined) {
 | 
			
		||||
@@ -391,7 +396,6 @@ export class SwapQuoter {
 | 
			
		||||
            marketOperation,
 | 
			
		||||
            assetFillAmount,
 | 
			
		||||
            gasPrice,
 | 
			
		||||
            opts.gasSchedule,
 | 
			
		||||
            opts.bridgeSlippage,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@@ -494,7 +498,6 @@ function createSwapQuote(
 | 
			
		||||
    operation: MarketOperation,
 | 
			
		||||
    assetFillAmount: BigNumber,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
    gasSchedule: FeeSchedule,
 | 
			
		||||
    slippage: number,
 | 
			
		||||
): SwapQuote {
 | 
			
		||||
    const {
 | 
			
		||||
@@ -509,8 +512,8 @@ function createSwapQuote(
 | 
			
		||||
 | 
			
		||||
    // Calculate quote info
 | 
			
		||||
    const { bestCaseQuoteInfo, worstCaseQuoteInfo, sourceBreakdown } = isTwoHop
 | 
			
		||||
        ? calculateTwoHopQuoteInfo(optimizedOrders, operation, gasSchedule, slippage)
 | 
			
		||||
        : calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, gasSchedule, slippage);
 | 
			
		||||
        ? calculateTwoHopQuoteInfo(optimizedOrders, operation, slippage)
 | 
			
		||||
        : calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, slippage);
 | 
			
		||||
 | 
			
		||||
    // Put together the swap quote
 | 
			
		||||
    const { makerTokenDecimals, takerTokenDecimals } = optimizerResult.marketSideLiquidity;
 | 
			
		||||
@@ -551,7 +554,6 @@ function calculateQuoteInfo(
 | 
			
		||||
    operation: MarketOperation,
 | 
			
		||||
    assetFillAmount: BigNumber,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
    gasSchedule: FeeSchedule,
 | 
			
		||||
    slippage: number,
 | 
			
		||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
 | 
			
		||||
    const bestCaseFillResult = simulateBestCaseFill({
 | 
			
		||||
@@ -559,7 +561,7 @@ function calculateQuoteInfo(
 | 
			
		||||
        orders: optimizedOrders,
 | 
			
		||||
        side: operation,
 | 
			
		||||
        fillAmount: assetFillAmount,
 | 
			
		||||
        opts: { gasSchedule },
 | 
			
		||||
        opts: {},
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const worstCaseFillResult = simulateWorstCaseFill({
 | 
			
		||||
@@ -567,7 +569,7 @@ function calculateQuoteInfo(
 | 
			
		||||
        orders: optimizedOrders,
 | 
			
		||||
        side: operation,
 | 
			
		||||
        fillAmount: assetFillAmount,
 | 
			
		||||
        opts: { gasSchedule, slippage },
 | 
			
		||||
        opts: { slippage },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
@@ -580,18 +582,12 @@ function calculateQuoteInfo(
 | 
			
		||||
function calculateTwoHopQuoteInfo(
 | 
			
		||||
    optimizedOrders: OptimizedMarketOrder[],
 | 
			
		||||
    operation: MarketOperation,
 | 
			
		||||
    gasSchedule: FeeSchedule,
 | 
			
		||||
    slippage: number,
 | 
			
		||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
 | 
			
		||||
    const [firstHopOrder, secondHopOrder] = optimizedOrders;
 | 
			
		||||
    const [firstHopFill] = firstHopOrder.fills;
 | 
			
		||||
    const [secondHopFill] = secondHopOrder.fills;
 | 
			
		||||
    const gas = new BigNumber(
 | 
			
		||||
        gasSchedule[ERC20BridgeSource.MultiHop]!({
 | 
			
		||||
            firstHopSource: _.pick(firstHopFill, 'source', 'fillData'),
 | 
			
		||||
            secondHopSource: _.pick(secondHopFill, 'source', 'fillData'),
 | 
			
		||||
        }),
 | 
			
		||||
    ).toNumber();
 | 
			
		||||
    const gas = (firstHopOrder.gasUsed || ZERO_AMOUNT).plus(secondHopFill.gasUsed || ZERO_AMOUNT).toNumber();
 | 
			
		||||
    return {
 | 
			
		||||
        bestCaseQuoteInfo: {
 | 
			
		||||
            makerAmount: operation === MarketOperation.Sell ? secondHopFill.output : secondHopFill.input,
 | 
			
		||||
 
 | 
			
		||||
@@ -256,7 +256,6 @@ export interface RfqRequestOpts {
 | 
			
		||||
 * gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
 | 
			
		||||
 */
 | 
			
		||||
export interface SwapQuoteRequestOpts extends GetMarketOrdersOpts {
 | 
			
		||||
    gasPrice?: BigNumber;
 | 
			
		||||
    rfqt?: RfqRequestOpts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,8 @@ import {
 | 
			
		||||
    CURVE_V2_POLYGON_INFOS,
 | 
			
		||||
    DFYN_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    ELLIPSIS_BSC_INFOS,
 | 
			
		||||
    FIREBIRDONESWAP_BSC_INFOS,
 | 
			
		||||
    FIREBIRDONESWAP_POLYGON_INFOS,
 | 
			
		||||
    JULSWAP_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    KYBER_BANNED_RESERVES,
 | 
			
		||||
    KYBER_BRIDGED_LIQUIDITY_PREFIX,
 | 
			
		||||
@@ -36,7 +38,7 @@ import {
 | 
			
		||||
    SUSHISWAP_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    SWERVE_MAINNET_INFOS,
 | 
			
		||||
    UNISWAPV2_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    WAULT_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    WAULTSWAP_ROUTER_BY_CHAIN_ID,
 | 
			
		||||
    XSIGMA_MAINNET_INFOS,
 | 
			
		||||
} from './constants';
 | 
			
		||||
import { CurveInfo, ERC20BridgeSource } from './types';
 | 
			
		||||
@@ -195,6 +197,30 @@ export function getNerveInfosForPair(chainId: ChainId, takerToken: string, maker
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getFirebirdOneSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
 | 
			
		||||
    if (chainId === ChainId.BSC) {
 | 
			
		||||
        return Object.values(FIREBIRDONESWAP_BSC_INFOS).filter(c =>
 | 
			
		||||
            [makerToken, takerToken].every(
 | 
			
		||||
                t =>
 | 
			
		||||
                    (c.tokens.includes(t) && c.metaTokens === undefined) ||
 | 
			
		||||
                    (c.tokens.includes(t) &&
 | 
			
		||||
                        [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
    } else if (chainId === ChainId.Polygon) {
 | 
			
		||||
        return Object.values(FIREBIRDONESWAP_POLYGON_INFOS).filter(c =>
 | 
			
		||||
            [makerToken, takerToken].every(
 | 
			
		||||
                t =>
 | 
			
		||||
                    (c.tokens.includes(t) && c.metaTokens === undefined) ||
 | 
			
		||||
                    (c.tokens.includes(t) &&
 | 
			
		||||
                        [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
 | 
			
		||||
            ),
 | 
			
		||||
        );
 | 
			
		||||
    } else {
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getBeltInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
 | 
			
		||||
    if (chainId !== ChainId.BSC) {
 | 
			
		||||
        return [];
 | 
			
		||||
@@ -308,7 +334,8 @@ export function getCurveLikeInfosForPair(
 | 
			
		||||
        | ERC20BridgeSource.Ellipsis
 | 
			
		||||
        | ERC20BridgeSource.Smoothy
 | 
			
		||||
        | ERC20BridgeSource.Saddle
 | 
			
		||||
        | ERC20BridgeSource.XSigma,
 | 
			
		||||
        | ERC20BridgeSource.XSigma
 | 
			
		||||
        | ERC20BridgeSource.FirebirdOneSwap,
 | 
			
		||||
): CurveDetailedInfo[] {
 | 
			
		||||
    let pools: CurveInfo[] = [];
 | 
			
		||||
    switch (source) {
 | 
			
		||||
@@ -342,6 +369,9 @@ export function getCurveLikeInfosForPair(
 | 
			
		||||
        case ERC20BridgeSource.XSigma:
 | 
			
		||||
            pools = getXSigmaInfosForPair(chainId, takerToken, makerToken);
 | 
			
		||||
            break;
 | 
			
		||||
        case ERC20BridgeSource.FirebirdOneSwap:
 | 
			
		||||
            pools = getFirebirdOneSwapInfosForPair(chainId, takerToken, makerToken);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw new Error(`Unknown Curve like source ${source}`);
 | 
			
		||||
    }
 | 
			
		||||
@@ -399,7 +429,7 @@ export function uniswapV2LikeRouterAddress(
 | 
			
		||||
        case ERC20BridgeSource.Dfyn:
 | 
			
		||||
            return DFYN_ROUTER_BY_CHAIN_ID[chainId];
 | 
			
		||||
        case ERC20BridgeSource.WaultSwap:
 | 
			
		||||
            return WAULT_ROUTER_BY_CHAIN_ID[chainId];
 | 
			
		||||
            return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId];
 | 
			
		||||
        case ERC20BridgeSource.Polydex:
 | 
			
		||||
            return POLYDEX_ROUTER_BY_CHAIN_ID[chainId];
 | 
			
		||||
        default:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,11 @@
 | 
			
		||||
import { Web3Wrapper } from '@0x/dev-utils';
 | 
			
		||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
 | 
			
		||||
import { BigNumber, logUtils } from '@0x/utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { MarketOperation } from '../../types';
 | 
			
		||||
 | 
			
		||||
import { COMPARISON_PRICE_DECIMALS, SOURCE_FLAGS } from './constants';
 | 
			
		||||
import {
 | 
			
		||||
    ComparisonPrice,
 | 
			
		||||
    ERC20BridgeSource,
 | 
			
		||||
    ExchangeProxyOverhead,
 | 
			
		||||
    FeeEstimate,
 | 
			
		||||
    FeeSchedule,
 | 
			
		||||
    MarketSideLiquidity,
 | 
			
		||||
} from './types';
 | 
			
		||||
import { COMPARISON_PRICE_DECIMALS, NATIVE_RFQT_GAS_USED, SOURCE_FLAGS } from './constants';
 | 
			
		||||
import { ComparisonPrice, ExchangeProxyOverhead, MarketSideLiquidity } from './types';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Takes in an optimizer response and returns a price for RFQT MMs to beat
 | 
			
		||||
@@ -22,42 +14,21 @@ import {
 | 
			
		||||
 * @param adjustedRate the adjusted rate (accounting for fees) from the optimizer, maker/taker
 | 
			
		||||
 * @param amount the amount specified by the client
 | 
			
		||||
 * @param marketSideLiquidity the results from querying liquidity sources
 | 
			
		||||
 * @param feeSchedule the fee schedule passed to the Optimizer
 | 
			
		||||
 * @param gasPrice the gas price used to calculate costs
 | 
			
		||||
 * @return ComparisonPrice object with the prices for RFQ MMs to beat
 | 
			
		||||
 */
 | 
			
		||||
export function getComparisonPrices(
 | 
			
		||||
    adjustedRate: BigNumber,
 | 
			
		||||
    amount: BigNumber,
 | 
			
		||||
    marketSideLiquidity: MarketSideLiquidity,
 | 
			
		||||
    feeSchedule: FeeSchedule,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
    exchangeProxyOverhead: ExchangeProxyOverhead,
 | 
			
		||||
): ComparisonPrice {
 | 
			
		||||
    let wholeOrder: BigNumber | undefined;
 | 
			
		||||
    let feeInEth: BigNumber | number;
 | 
			
		||||
 | 
			
		||||
    // HACK: get the fee penalty of a single 0x native order
 | 
			
		||||
    // The FeeSchedule function takes in a `FillData` object and returns a fee estimate in ETH
 | 
			
		||||
    // We don't have fill data here, we just want the cost of a single native order, so we pass in undefined
 | 
			
		||||
    // This works because the feeSchedule returns a constant for Native orders, this will need
 | 
			
		||||
    // to be tweaked if the feeSchedule for native orders uses the fillData passed in
 | 
			
		||||
    // 2 potential issues: there is no native fee schedule or the fee schedule depends on fill data
 | 
			
		||||
    if (feeSchedule[ERC20BridgeSource.Native] === undefined) {
 | 
			
		||||
        logUtils.warn('ComparisonPrice function did not find native order fee schedule');
 | 
			
		||||
 | 
			
		||||
        return { wholeOrder };
 | 
			
		||||
    } else {
 | 
			
		||||
        try {
 | 
			
		||||
            const fillFeeInEth = new BigNumber(
 | 
			
		||||
                (feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }),
 | 
			
		||||
            );
 | 
			
		||||
            const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
 | 
			
		||||
            feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);
 | 
			
		||||
        } catch {
 | 
			
		||||
            logUtils.warn('Native order fee schedule requires fill data');
 | 
			
		||||
 | 
			
		||||
            return { wholeOrder };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // Assuming EP overhead has been gas price adjusted
 | 
			
		||||
    feeInEth = NATIVE_RFQT_GAS_USED.times(gasPrice).plus(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
 | 
			
		||||
 | 
			
		||||
    // Calc native order fee penalty in output unit (maker units for sells, taker unit for buys)
 | 
			
		||||
    const feePenalty = !marketSideLiquidity.outputAmountPerEth.isZero()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
 | 
			
		||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
 | 
			
		||||
import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { formatBytes32String } from '@ethersproject/strings';
 | 
			
		||||
 | 
			
		||||
@@ -7,25 +6,15 @@ import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
 | 
			
		||||
 | 
			
		||||
import { SourceFilters } from './source_filters';
 | 
			
		||||
import {
 | 
			
		||||
    BancorFillData,
 | 
			
		||||
    CurveFillData,
 | 
			
		||||
    CurveFunctionSelectors,
 | 
			
		||||
    CurveInfo,
 | 
			
		||||
    DODOFillData,
 | 
			
		||||
    ERC20BridgeSource,
 | 
			
		||||
    FeeSchedule,
 | 
			
		||||
    FillData,
 | 
			
		||||
    GetMarketOrdersOpts,
 | 
			
		||||
    KyberSamplerOpts,
 | 
			
		||||
    LidoInfo,
 | 
			
		||||
    LiquidityProviderFillData,
 | 
			
		||||
    LiquidityProviderRegistry,
 | 
			
		||||
    MakerPsmFillData,
 | 
			
		||||
    MultiHopFillData,
 | 
			
		||||
    PsmInfo,
 | 
			
		||||
    TokenAdjacencyGraph,
 | 
			
		||||
    UniswapV2FillData,
 | 
			
		||||
    UniswapV3FillData,
 | 
			
		||||
} from './types';
 | 
			
		||||
 | 
			
		||||
// tslint:disable: custom-no-magic-numbers no-bitwise
 | 
			
		||||
@@ -41,7 +30,7 @@ export const ONE_HOUR_IN_SECONDS = 60 * 60;
 | 
			
		||||
export const ONE_SECOND_MS = 1000;
 | 
			
		||||
export const NULL_BYTES = '0x';
 | 
			
		||||
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
 | 
			
		||||
export const SAMPLER_ADDRESS = '0x5555555555555555555555555555555555555555';
 | 
			
		||||
export const SAMPLER_ADDRESS = '0x5555555555555555555555555555555555555556';
 | 
			
		||||
export const COMPARISON_PRICE_DECIMALS = 10;
 | 
			
		||||
 | 
			
		||||
// TODO(kimpers): Consolidate this implementation with the one in @0x/token-metadata
 | 
			
		||||
@@ -130,6 +119,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
 | 
			
		||||
            ERC20BridgeSource.JulSwap,
 | 
			
		||||
            ERC20BridgeSource.LiquidityProvider,
 | 
			
		||||
            ERC20BridgeSource.WaultSwap,
 | 
			
		||||
            ERC20BridgeSource.FirebirdOneSwap,
 | 
			
		||||
        ]),
 | 
			
		||||
        [ChainId.Polygon]: new SourceFilters([
 | 
			
		||||
            ERC20BridgeSource.SushiSwap,
 | 
			
		||||
@@ -143,6 +133,8 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
 | 
			
		||||
            ERC20BridgeSource.CurveV2,
 | 
			
		||||
            ERC20BridgeSource.WaultSwap,
 | 
			
		||||
            ERC20BridgeSource.Polydex,
 | 
			
		||||
            ERC20BridgeSource.ApeSwap,
 | 
			
		||||
            ERC20BridgeSource.FirebirdOneSwap,
 | 
			
		||||
        ]),
 | 
			
		||||
    },
 | 
			
		||||
    new SourceFilters([]),
 | 
			
		||||
@@ -218,6 +210,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
 | 
			
		||||
            ERC20BridgeSource.JulSwap,
 | 
			
		||||
            ERC20BridgeSource.LiquidityProvider,
 | 
			
		||||
            ERC20BridgeSource.WaultSwap,
 | 
			
		||||
            ERC20BridgeSource.FirebirdOneSwap,
 | 
			
		||||
        ]),
 | 
			
		||||
        [ChainId.Polygon]: new SourceFilters([
 | 
			
		||||
            ERC20BridgeSource.SushiSwap,
 | 
			
		||||
@@ -231,6 +224,8 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
 | 
			
		||||
            ERC20BridgeSource.CurveV2,
 | 
			
		||||
            ERC20BridgeSource.WaultSwap,
 | 
			
		||||
            ERC20BridgeSource.Polydex,
 | 
			
		||||
            ERC20BridgeSource.ApeSwap,
 | 
			
		||||
            ERC20BridgeSource.FirebirdOneSwap,
 | 
			
		||||
        ]),
 | 
			
		||||
    },
 | 
			
		||||
    new SourceFilters([]),
 | 
			
		||||
@@ -256,13 +251,13 @@ export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>
 | 
			
		||||
 | 
			
		||||
// HACK(mzhu25): Limit and RFQ orders need to be treated as different sources
 | 
			
		||||
//               when computing the exchange proxy gas overhead.
 | 
			
		||||
export const SOURCE_FLAGS: { [key in ERC20BridgeSource]: number } & {
 | 
			
		||||
    RfqOrder: number;
 | 
			
		||||
    LimitOrder: number;
 | 
			
		||||
export const SOURCE_FLAGS: { [key in ERC20BridgeSource]: bigint } & {
 | 
			
		||||
    RfqOrder: bigint;
 | 
			
		||||
    LimitOrder: bigint;
 | 
			
		||||
} = Object.assign(
 | 
			
		||||
    {},
 | 
			
		||||
    ...['RfqOrder', 'LimitOrder', ...Object.values(ERC20BridgeSource)].map((source, index) => ({
 | 
			
		||||
        [source]: source === ERC20BridgeSource.Native ? 0 : 1 << index,
 | 
			
		||||
        [source]: source === ERC20BridgeSource.Native ? BigInt(0) : BigInt(1) << BigInt(index),
 | 
			
		||||
    })),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@@ -350,12 +345,15 @@ export const MAINNET_TOKENS = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const BSC_TOKENS = {
 | 
			
		||||
    WBNB: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c',
 | 
			
		||||
    BUSD: '0xe9e7cea3dedca5984780bafc599bd69add087d56',
 | 
			
		||||
    USDT: '0x55d398326f99059ff775485246999027b3197955',
 | 
			
		||||
    USDC: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
 | 
			
		||||
    DAI: '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3',
 | 
			
		||||
    PAX: '0xb7f8cd00c5a06c0537e2abff0b58033d02e5e094',
 | 
			
		||||
    UST: '0x23396cf899ca06c4472205fc903bdb4de249d6fc',
 | 
			
		||||
    WEX: '0xa9c41a46a6b3531d28d5c32f6633dd2ff05dfb90',
 | 
			
		||||
    WETH: '0x2170ed0880ac9a755fd29b2688956bd959f933f8',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const POLYGON_TOKENS = {
 | 
			
		||||
@@ -370,8 +368,9 @@ export const POLYGON_TOKENS = {
 | 
			
		||||
    WETH: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619',
 | 
			
		||||
    renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501',
 | 
			
		||||
    QUICK: '0x831753dd7087cac61ab5644b308642cc1c33dc13',
 | 
			
		||||
    TITAN: '0xaaa5b9e6c589642f98a1cda99b9d024b8407285a',
 | 
			
		||||
    IRON: '0xd86b5923f3ad7b585ed81b448170ae026c65ae9a',
 | 
			
		||||
    DFYN: '0xc168e40227e4ebd8c1cae80f7a55a4f0e6d66c97',
 | 
			
		||||
    BANANA: '0x5d47baba0d66083c52009271faf3f50dcc01023c',
 | 
			
		||||
    WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CURVE_POOLS = {
 | 
			
		||||
@@ -469,6 +468,14 @@ export const XSIGMA_POOLS = {
 | 
			
		||||
    stable: '0x3333333ACdEdBbC9Ad7bda0876e60714195681c5',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const FIREBIRDONESWAP_BSC_POOLS = {
 | 
			
		||||
    oneswap: '0x01c9475dbd36e46d1961572c8de24b74616bae9e',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const FIREBIRDONESWAP_POLYGON_POOLS = {
 | 
			
		||||
    oneswap: '0x01c9475dbd36e46d1961572c8de24b74616bae9e',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
 | 
			
		||||
    {
 | 
			
		||||
        [ChainId.Mainnet]: [
 | 
			
		||||
@@ -479,12 +486,13 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
 | 
			
		||||
            MAINNET_TOKENS.WBTC,
 | 
			
		||||
        ],
 | 
			
		||||
        [ChainId.BSC]: [
 | 
			
		||||
            '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB
 | 
			
		||||
            '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD
 | 
			
		||||
            '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3', // DAI
 | 
			
		||||
            '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', // USDC
 | 
			
		||||
            '0x2170ed0880ac9a755fd29b2688956bd959f933f8', // ETH
 | 
			
		||||
            '0x55d398326f99059ff775485246999027b3197955', // BUSD-T
 | 
			
		||||
            BSC_TOKENS.WBNB,
 | 
			
		||||
            BSC_TOKENS.BUSD,
 | 
			
		||||
            BSC_TOKENS.DAI,
 | 
			
		||||
            BSC_TOKENS.USDC,
 | 
			
		||||
            BSC_TOKENS.WETH,
 | 
			
		||||
            BSC_TOKENS.USDT,
 | 
			
		||||
            BSC_TOKENS.WEX,
 | 
			
		||||
        ],
 | 
			
		||||
        [ChainId.Ropsten]: [
 | 
			
		||||
            getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken,
 | 
			
		||||
@@ -499,8 +507,9 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
 | 
			
		||||
            POLYGON_TOKENS.USDT,
 | 
			
		||||
            POLYGON_TOKENS.WBTC,
 | 
			
		||||
            POLYGON_TOKENS.QUICK,
 | 
			
		||||
            POLYGON_TOKENS.TITAN,
 | 
			
		||||
            POLYGON_TOKENS.IRON,
 | 
			
		||||
            POLYGON_TOKENS.DFYN,
 | 
			
		||||
            POLYGON_TOKENS.BANANA,
 | 
			
		||||
            POLYGON_TOKENS.WEXPOLY,
 | 
			
		||||
        ],
 | 
			
		||||
    },
 | 
			
		||||
    [],
 | 
			
		||||
@@ -557,8 +566,6 @@ const CURVE_POLYGON_ATRICRYPTO_TOKENS = [POLYGON_TOKENS.amDAI, POLYGON_TOKENS.am
 | 
			
		||||
 | 
			
		||||
const createCurveExchangePool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: info.tokens,
 | 
			
		||||
    metaTokens: undefined,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -567,8 +574,6 @@ const createCurveExchangePool = (info: { tokens: string[]; pool: string; gasSche
 | 
			
		||||
 | 
			
		||||
const createCurveExchangeUnderlyingPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: info.tokens,
 | 
			
		||||
    metaTokens: undefined,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -577,8 +582,6 @@ const createCurveExchangeUnderlyingPool = (info: { tokens: string[]; pool: strin
 | 
			
		||||
 | 
			
		||||
const createCurveMetaTriPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: [...info.tokens, ...CURVE_TRI_POOL_MAINNET_TOKENS],
 | 
			
		||||
    metaTokens: info.tokens,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -587,8 +590,6 @@ const createCurveMetaTriPool = (info: { tokens: string[]; pool: string; gasSched
 | 
			
		||||
 | 
			
		||||
const createCurveMetaTriBtcPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: [...info.tokens, ...CURVE_TRI_BTC_POOL_TOKEN],
 | 
			
		||||
    metaTokens: info.tokens,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -597,8 +598,6 @@ const createCurveMetaTriBtcPool = (info: { tokens: string[]; pool: string; gasSc
 | 
			
		||||
 | 
			
		||||
const createCurveExchangeV2Pool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange_v2,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_v2,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: info.tokens,
 | 
			
		||||
    metaTokens: undefined,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -607,8 +606,6 @@ const createCurveExchangeV2Pool = (info: { tokens: string[]; pool: string; gasSc
 | 
			
		||||
 | 
			
		||||
const createCurveV2MetaTriPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_v2,
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_underlying_v2,
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
    tokens: [...CURVE_POLYGON_ATRICRYPTO_UNDERLYING_TOKENS, ...info.tokens],
 | 
			
		||||
    metaTokens: info.tokens,
 | 
			
		||||
    poolAddress: info.pool,
 | 
			
		||||
@@ -886,12 +883,10 @@ export const XSIGMA_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Curve pools like using custom selectors
 | 
			
		||||
// Curve-like sources using custom selectors
 | 
			
		||||
export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [SADDLE_POOLS.stables]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap,
 | 
			
		||||
        sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
 | 
			
		||||
        buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
        poolAddress: SADDLE_POOLS.stables,
 | 
			
		||||
        tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
@@ -899,8 +894,6 @@ export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    },
 | 
			
		||||
    [SADDLE_POOLS.bitcoins]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap,
 | 
			
		||||
        sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
 | 
			
		||||
        buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
        poolAddress: SADDLE_POOLS.bitcoins,
 | 
			
		||||
        tokens: [MAINNET_TOKENS.tBTC, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.sBTC],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
@@ -911,8 +904,6 @@ export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
export const SMOOTHY_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [SMOOTHY_POOLS.syUSD]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap_uint256,
 | 
			
		||||
        sellQuoteFunctionSelector: CurveFunctionSelectors.get_swap_amount,
 | 
			
		||||
        buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
        poolAddress: SMOOTHY_POOLS.syUSD,
 | 
			
		||||
        tokens: [
 | 
			
		||||
            MAINNET_TOKENS.USDT,
 | 
			
		||||
@@ -932,8 +923,6 @@ export const SMOOTHY_MAINNET_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
export const SMOOTHY_BSC_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [SMOOTHY_POOLS.syUSD]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap_uint256,
 | 
			
		||||
        sellQuoteFunctionSelector: CurveFunctionSelectors.get_swap_amount,
 | 
			
		||||
        buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
        poolAddress: SMOOTHY_POOLS.syUSD,
 | 
			
		||||
        tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.USDC, BSC_TOKENS.DAI, BSC_TOKENS.PAX, BSC_TOKENS.UST],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
@@ -944,8 +933,6 @@ export const SMOOTHY_BSC_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [NERVE_POOLS.threePool]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap,
 | 
			
		||||
        sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
 | 
			
		||||
        buyQuoteFunctionSelector: CurveFunctionSelectors.None,
 | 
			
		||||
        poolAddress: NERVE_POOLS.threePool,
 | 
			
		||||
        tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.USDC],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
@@ -953,6 +940,26 @@ export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const FIREBIRDONESWAP_BSC_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [FIREBIRDONESWAP_BSC_POOLS.oneswap]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap,
 | 
			
		||||
        poolAddress: FIREBIRDONESWAP_BSC_POOLS.oneswap,
 | 
			
		||||
        tokens: [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.DAI, BSC_TOKENS.USDC],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
        gasSchedule: 100e3,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const FIREBIRDONESWAP_POLYGON_INFOS: { [name: string]: CurveInfo } = {
 | 
			
		||||
    [FIREBIRDONESWAP_POLYGON_POOLS.oneswap]: {
 | 
			
		||||
        exchangeFunctionSelector: CurveFunctionSelectors.swap,
 | 
			
		||||
        poolAddress: FIREBIRDONESWAP_POLYGON_POOLS.oneswap,
 | 
			
		||||
        tokens: [POLYGON_TOKENS.DAI, POLYGON_TOKENS.USDC, POLYGON_TOKENS.USDT],
 | 
			
		||||
        metaTokens: undefined,
 | 
			
		||||
        gasSchedule: 100e3,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Kyber reserve prefixes
 | 
			
		||||
 * 0xff Fed price reserve
 | 
			
		||||
@@ -1097,11 +1104,7 @@ export const KYBER_DMM_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
 | 
			
		||||
export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId(
 | 
			
		||||
    {
 | 
			
		||||
        [ChainId.Mainnet]: [
 | 
			
		||||
            '0x71CD6666064C3A1354a3B4dca5fA1E2D3ee7D303',
 | 
			
		||||
            '0xc4a8b7e29e3c8ec560cd4945c1cf3461a85a148d',
 | 
			
		||||
            '0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643',
 | 
			
		||||
        ],
 | 
			
		||||
        [ChainId.Mainnet]: ['0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643'],
 | 
			
		||||
        [ChainId.BSC]: ['0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64'],
 | 
			
		||||
    },
 | 
			
		||||
    [] as string[],
 | 
			
		||||
@@ -1302,6 +1305,7 @@ export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
export const APESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
    {
 | 
			
		||||
        [ChainId.BSC]: '0xc0788a3ad43d79aa53b09c2eacc313a787d1d607',
 | 
			
		||||
        [ChainId.Polygon]: '0xc0788a3ad43d79aa53b09c2eacc313a787d1d607',
 | 
			
		||||
    },
 | 
			
		||||
    NULL_ADDRESS,
 | 
			
		||||
);
 | 
			
		||||
@@ -1351,7 +1355,7 @@ export const DFYN_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
    NULL_ADDRESS,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const WAULT_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
export const WAULTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
    {
 | 
			
		||||
        [ChainId.BSC]: '0xd48745e39bbed146eec15b79cbf964884f9877c2',
 | 
			
		||||
        [ChainId.Polygon]: '0x3a1d87f206d12415f5b0a33e786967680aab4f6d',
 | 
			
		||||
@@ -1366,131 +1370,8 @@ export const POLYDEX_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
 | 
			
		||||
    NULL_ADDRESS,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
 | 
			
		||||
    // TODO: Different base cost if to/from ETH.
 | 
			
		||||
    let gas = 90e3;
 | 
			
		||||
    const path = (fillData as UniswapV2FillData).tokenAddressPath;
 | 
			
		||||
    if (path.length > 2) {
 | 
			
		||||
        gas += (path.length - 2) * 60e3; // +60k for each hop.
 | 
			
		||||
    }
 | 
			
		||||
    return gas;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculated gross gas cost of the underlying exchange.
 | 
			
		||||
 * The cost of switching from one source to another, assuming
 | 
			
		||||
 * we are in the middle of a transaction.
 | 
			
		||||
 * I.e remove the overhead cost of ExchangeProxy (130k) and
 | 
			
		||||
 * the ethereum transaction cost (21k)
 | 
			
		||||
 */
 | 
			
		||||
// tslint:disable:custom-no-magic-numbers
 | 
			
		||||
export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
 | 
			
		||||
    [ERC20BridgeSource.Native]: fillData => {
 | 
			
		||||
        // TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder
 | 
			
		||||
        const nativeFillData = fillData as { type: FillQuoteTransformerOrderType };
 | 
			
		||||
        return nativeFillData && nativeFillData.type === FillQuoteTransformerOrderType.Limit
 | 
			
		||||
            ? PROTOCOL_FEE_MULTIPLIER.plus(100e3).toNumber()
 | 
			
		||||
            : // TODO jacob revisit wth v4 LimitOrders
 | 
			
		||||
              100e3;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.Uniswap]: () => 90e3,
 | 
			
		||||
    [ERC20BridgeSource.LiquidityProvider]: fillData => {
 | 
			
		||||
        return (fillData as LiquidityProviderFillData).gasCost || 100e3;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.Eth2Dai]: () => 400e3,
 | 
			
		||||
    [ERC20BridgeSource.Kyber]: () => 450e3,
 | 
			
		||||
    [ERC20BridgeSource.Curve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.CurveV2]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Swerve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.SnowSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Nerve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Belt]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Ellipsis]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Smoothy]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Saddle]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.XSigma]: fillData => (fillData as CurveFillData).pool.gasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.MultiBridge]: () => 350e3,
 | 
			
		||||
    [ERC20BridgeSource.UniswapV2]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.SushiSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.CryptoCom]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Linkswap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Balancer]: () => 120e3,
 | 
			
		||||
    [ERC20BridgeSource.BalancerV2]: () => 100e3,
 | 
			
		||||
    [ERC20BridgeSource.Cream]: () => 120e3,
 | 
			
		||||
    [ERC20BridgeSource.MStable]: () => 200e3,
 | 
			
		||||
    [ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {
 | 
			
		||||
        const psmFillData = fillData as MakerPsmFillData;
 | 
			
		||||
        return psmFillData.takerToken === psmFillData.gemTokenAddress ? 210e3 : 290e3;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.Mooniswap]: () => 130e3,
 | 
			
		||||
    [ERC20BridgeSource.Shell]: () => 170e3,
 | 
			
		||||
    [ERC20BridgeSource.Component]: () => 188e3,
 | 
			
		||||
    [ERC20BridgeSource.MultiHop]: (fillData?: FillData) => {
 | 
			
		||||
        const firstHop = (fillData as MultiHopFillData).firstHopSource;
 | 
			
		||||
        const secondHop = (fillData as MultiHopFillData).secondHopSource;
 | 
			
		||||
        const firstHopGas = DEFAULT_GAS_SCHEDULE[firstHop.source](firstHop.fillData);
 | 
			
		||||
        const secondHopGas = DEFAULT_GAS_SCHEDULE[secondHop.source](secondHop.fillData);
 | 
			
		||||
        return new BigNumber(firstHopGas)
 | 
			
		||||
            .plus(secondHopGas)
 | 
			
		||||
            .plus(30e3)
 | 
			
		||||
            .toNumber();
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.Dodo]: (fillData?: FillData) => {
 | 
			
		||||
        const isSellBase = (fillData as DODOFillData).isSellBase;
 | 
			
		||||
        // Sell base is cheaper as it is natively supported
 | 
			
		||||
        // sell quote requires additional calculation and overhead
 | 
			
		||||
        return isSellBase ? 180e3 : 300e3;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3,
 | 
			
		||||
    [ERC20BridgeSource.Bancor]: (fillData?: FillData) => {
 | 
			
		||||
        let gas = 200e3;
 | 
			
		||||
        const path = (fillData as BancorFillData).path;
 | 
			
		||||
        if (path.length > 2) {
 | 
			
		||||
            gas += (path.length - 2) * 60e3; // +60k for each hop.
 | 
			
		||||
        }
 | 
			
		||||
        return gas;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.KyberDmm]: (fillData?: FillData) => {
 | 
			
		||||
        // TODO: Different base cost if to/from ETH.
 | 
			
		||||
        let gas = 95e3;
 | 
			
		||||
        const path = (fillData as UniswapV2FillData).tokenAddressPath;
 | 
			
		||||
        if (path.length > 2) {
 | 
			
		||||
            gas += (path.length - 2) * 65e3; // +65k for each hop.
 | 
			
		||||
        }
 | 
			
		||||
        return gas;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => {
 | 
			
		||||
        let gas = 100e3;
 | 
			
		||||
        const path = (fillData as UniswapV3FillData).tokenAddressPath;
 | 
			
		||||
        if (path.length > 2) {
 | 
			
		||||
            gas += (path.length - 2) * 32e3; // +32k for each hop.
 | 
			
		||||
        }
 | 
			
		||||
        return gas;
 | 
			
		||||
    },
 | 
			
		||||
    [ERC20BridgeSource.Lido]: () => 226e3,
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // BSC
 | 
			
		||||
    //
 | 
			
		||||
    [ERC20BridgeSource.PancakeSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.PancakeSwapV2]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.BakerySwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.ApeSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.CafeSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.CheeseSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.JulSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.WaultSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // Polygon
 | 
			
		||||
    //
 | 
			
		||||
    [ERC20BridgeSource.QuickSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.ComethSwap]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Dfyn]: uniswapV2CloneGasSchedule,
 | 
			
		||||
    [ERC20BridgeSource.Polydex]: uniswapV2CloneGasSchedule,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };
 | 
			
		||||
export const NATIVE_RFQT_GAS_USED = new BigNumber(100e3);
 | 
			
		||||
export const NATIVE_LIMIT_ORDER_GAS_USED = NATIVE_RFQT_GAS_USED.plus(PROTOCOL_FEE_MULTIPLIER);
 | 
			
		||||
 | 
			
		||||
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
 | 
			
		||||
 | 
			
		||||
@@ -1506,11 +1387,10 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
 | 
			
		||||
    maxFallbackSlippage: 0.05,
 | 
			
		||||
    numSamples: 13,
 | 
			
		||||
    sampleDistributionBase: 1.05,
 | 
			
		||||
    feeSchedule: DEFAULT_FEE_SCHEDULE,
 | 
			
		||||
    gasSchedule: DEFAULT_GAS_SCHEDULE,
 | 
			
		||||
    exchangeProxyOverhead: () => ZERO_AMOUNT,
 | 
			
		||||
    allowFallback: true,
 | 
			
		||||
    shouldGenerateQuoteReport: true,
 | 
			
		||||
    shouldIncludePriceComparisonsReport: false,
 | 
			
		||||
    tokenAdjacencyGraph: { default: [] },
 | 
			
		||||
    gasPrice: new BigNumber(1e9),
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,14 @@ import { BigNumber, hexUtils } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
 | 
			
		||||
 | 
			
		||||
import { POSITIVE_INF, SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
 | 
			
		||||
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
 | 
			
		||||
import {
 | 
			
		||||
    NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
    NATIVE_RFQT_GAS_USED,
 | 
			
		||||
    POSITIVE_INF,
 | 
			
		||||
    SOURCE_FLAGS,
 | 
			
		||||
    ZERO_AMOUNT,
 | 
			
		||||
} from './constants';
 | 
			
		||||
import { DexSample, ERC20BridgeSource, Fill } from './types';
 | 
			
		||||
 | 
			
		||||
// tslint:disable: prefer-for-of no-bitwise completed-docs
 | 
			
		||||
 | 
			
		||||
@@ -13,17 +19,16 @@ import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
 | 
			
		||||
 */
 | 
			
		||||
export function createFills(opts: {
 | 
			
		||||
    side: MarketOperation;
 | 
			
		||||
    gasPrice: BigNumber;
 | 
			
		||||
    orders?: NativeOrderWithFillableAmounts[];
 | 
			
		||||
    dexQuotes?: DexSample[][];
 | 
			
		||||
    targetInput?: BigNumber;
 | 
			
		||||
    outputAmountPerEth?: BigNumber;
 | 
			
		||||
    inputAmountPerEth?: BigNumber;
 | 
			
		||||
    excludedSources?: ERC20BridgeSource[];
 | 
			
		||||
    feeSchedule?: FeeSchedule;
 | 
			
		||||
}): Fill[][] {
 | 
			
		||||
    const { side } = opts;
 | 
			
		||||
    const excludedSources = opts.excludedSources || [];
 | 
			
		||||
    const feeSchedule = opts.feeSchedule || {};
 | 
			
		||||
    const orders = opts.orders || [];
 | 
			
		||||
    const dexQuotes = opts.dexQuotes || [];
 | 
			
		||||
    const outputAmountPerEth = opts.outputAmountPerEth || ZERO_AMOUNT;
 | 
			
		||||
@@ -35,11 +40,11 @@ export function createFills(opts: {
 | 
			
		||||
        opts.targetInput,
 | 
			
		||||
        outputAmountPerEth,
 | 
			
		||||
        inputAmountPerEth,
 | 
			
		||||
        feeSchedule,
 | 
			
		||||
        opts.gasPrice,
 | 
			
		||||
    );
 | 
			
		||||
    // Create DEX fills.
 | 
			
		||||
    const dexFills = dexQuotes.map(singleSourceSamples =>
 | 
			
		||||
        dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, feeSchedule),
 | 
			
		||||
        dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, opts.gasPrice),
 | 
			
		||||
    );
 | 
			
		||||
    return [...dexFills, nativeFills]
 | 
			
		||||
        .map(p => clipFillsToInput(p, opts.targetInput))
 | 
			
		||||
@@ -77,7 +82,7 @@ function nativeOrdersToFills(
 | 
			
		||||
    targetInput: BigNumber = POSITIVE_INF,
 | 
			
		||||
    outputAmountPerEth: BigNumber,
 | 
			
		||||
    inputAmountPerEth: BigNumber,
 | 
			
		||||
    fees: FeeSchedule,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
): Fill[] {
 | 
			
		||||
    const sourcePathId = hexUtils.random();
 | 
			
		||||
    // Create a single path from all orders.
 | 
			
		||||
@@ -88,10 +93,12 @@ function nativeOrdersToFills(
 | 
			
		||||
        const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
 | 
			
		||||
        const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
 | 
			
		||||
        const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
 | 
			
		||||
        const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
 | 
			
		||||
        const gasUsed =
 | 
			
		||||
            o.type === FillQuoteTransformerOrderType.Limit ? NATIVE_LIMIT_ORDER_GAS_USED : NATIVE_RFQT_GAS_USED;
 | 
			
		||||
        const feeInEth = gasUsed.times(gasPrice);
 | 
			
		||||
        const outputPenalty = !outputAmountPerEth.isZero()
 | 
			
		||||
            ? outputAmountPerEth.times(fee)
 | 
			
		||||
            : inputAmountPerEth.times(fee).times(output.dividedToIntegerBy(input));
 | 
			
		||||
            ? outputAmountPerEth.times(feeInEth)
 | 
			
		||||
            : inputAmountPerEth.times(feeInEth).times(output.dividedToIntegerBy(input));
 | 
			
		||||
        // targetInput can be less than the order size
 | 
			
		||||
        // whilst the penalty is constant, it affects the adjusted output
 | 
			
		||||
        // only up until the target has been exhausted.
 | 
			
		||||
@@ -104,6 +111,7 @@ function nativeOrdersToFills(
 | 
			
		||||
            side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
 | 
			
		||||
        const adjustedRate =
 | 
			
		||||
            side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
 | 
			
		||||
 | 
			
		||||
        // Skip orders with rates that are <= 0.
 | 
			
		||||
        if (adjustedRate.lte(0)) {
 | 
			
		||||
            continue;
 | 
			
		||||
@@ -119,6 +127,7 @@ function nativeOrdersToFills(
 | 
			
		||||
            parent: undefined, // TBD
 | 
			
		||||
            source: ERC20BridgeSource.Native,
 | 
			
		||||
            type,
 | 
			
		||||
            gasUsed,
 | 
			
		||||
            fillData: { ...o },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -137,7 +146,7 @@ function dexSamplesToFills(
 | 
			
		||||
    samples: DexSample[],
 | 
			
		||||
    outputAmountPerEth: BigNumber,
 | 
			
		||||
    inputAmountPerEth: BigNumber,
 | 
			
		||||
    fees: FeeSchedule,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
): Fill[] {
 | 
			
		||||
    const sourcePathId = hexUtils.random();
 | 
			
		||||
    const fills: Fill[] = [];
 | 
			
		||||
@@ -152,7 +161,12 @@ function dexSamplesToFills(
 | 
			
		||||
        const { source, fillData } = sample;
 | 
			
		||||
        const input = sample.input.minus(prevSample ? prevSample.input : 0);
 | 
			
		||||
        const output = sample.output.minus(prevSample ? prevSample.output : 0);
 | 
			
		||||
        const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
 | 
			
		||||
 | 
			
		||||
        if (!sample.gasUsed || sample.gasUsed.isZero()) {
 | 
			
		||||
            throw new Error(`${sample.source} gas used missing or 0`);
 | 
			
		||||
        }
 | 
			
		||||
        const fee = gasPrice.times(sample.gasUsed);
 | 
			
		||||
 | 
			
		||||
        let penalty = ZERO_AMOUNT;
 | 
			
		||||
        if (i === 0) {
 | 
			
		||||
            // Only the first fill in a DEX path incurs a penalty.
 | 
			
		||||
@@ -173,6 +187,7 @@ function dexSamplesToFills(
 | 
			
		||||
            index: i,
 | 
			
		||||
            parent: i !== 0 ? fills[fills.length - 1] : undefined,
 | 
			
		||||
            flags: SOURCE_FLAGS[source],
 | 
			
		||||
            gasUsed: sample.gasUsed,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return fills;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
import { FillQuoteTransformerOrderType, RfqOrder } from '@0x/protocol-utils';
 | 
			
		||||
import { BigNumber, NULL_ADDRESS } from '@0x/utils';
 | 
			
		||||
import * as ethjs from 'ethereumjs-util';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { artifacts } from '../..';
 | 
			
		||||
import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants';
 | 
			
		||||
import {
 | 
			
		||||
    AssetSwapperContractAddresses,
 | 
			
		||||
@@ -36,7 +38,7 @@ import {
 | 
			
		||||
    ZERO_AMOUNT,
 | 
			
		||||
} from './constants';
 | 
			
		||||
import { createFills } from './fills';
 | 
			
		||||
import { getBestTwoHopQuote } from './multihop_utils';
 | 
			
		||||
import { getBestTwoHopQuote, getIntermediateTokens } from './multihop_utils';
 | 
			
		||||
import { createOrdersFromTwoHopSample } from './orders';
 | 
			
		||||
import { PathPenaltyOpts } from './path';
 | 
			
		||||
import { fillsToSortedPaths, findOptimalPathAsync } from './path_optimizer';
 | 
			
		||||
@@ -50,11 +52,21 @@ import {
 | 
			
		||||
    GenerateOptimizedOrdersOpts,
 | 
			
		||||
    GetMarketOrdersOpts,
 | 
			
		||||
    MarketSideLiquidity,
 | 
			
		||||
    MultiHopFillData,
 | 
			
		||||
    OptimizerResult,
 | 
			
		||||
    OptimizerResultWithReport,
 | 
			
		||||
    OrderDomain,
 | 
			
		||||
} from './types';
 | 
			
		||||
 | 
			
		||||
const HACKED_ERC20_BYTECODE = _.get(artifacts.HackedERC20, 'compilerOutput.evm.deployedBytecode.object');
 | 
			
		||||
const GAS_OVERHEAD_BYTECODE = _.get(artifacts.GasOverhead, 'compilerOutput.evm.deployedBytecode.object');
 | 
			
		||||
const DELEGEATE_HACKED_ERC20_BYTECODE = _.get(
 | 
			
		||||
    artifacts.DelegateHackedERC20,
 | 
			
		||||
    'compilerOutput.evm.deployedBytecode.object',
 | 
			
		||||
);
 | 
			
		||||
const HACKED_ERC20_ADDRESS = '0xDEf1000000000000000000000000000000DE7d37';
 | 
			
		||||
const GAS_OVERHEAD_ADDRESS = '0xDeF1000000000000000000000000000000001337';
 | 
			
		||||
 | 
			
		||||
// tslint:disable:boolean-naming
 | 
			
		||||
 | 
			
		||||
export class MarketOperationUtils {
 | 
			
		||||
@@ -63,6 +75,7 @@ export class MarketOperationUtils {
 | 
			
		||||
    private readonly _feeSources: SourceFilters;
 | 
			
		||||
    private readonly _nativeFeeToken: string;
 | 
			
		||||
    private readonly _nativeFeeTokenAmount: BigNumber;
 | 
			
		||||
    private readonly _contractCodeByAddress: { [address: string]: string | undefined } = {};
 | 
			
		||||
 | 
			
		||||
    private static _computeQuoteReport(
 | 
			
		||||
        quoteRequestor: QuoteRequestor | undefined,
 | 
			
		||||
@@ -131,34 +144,48 @@ export class MarketOperationUtils {
 | 
			
		||||
        // Used to determine whether the tx origin is an EOA or a contract
 | 
			
		||||
        const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
 | 
			
		||||
 | 
			
		||||
        const { overrides } = await this._fetchTokenOverridesAsync(takerToken, makerToken);
 | 
			
		||||
        // Call the sampler contract.
 | 
			
		||||
        const samplerPromise = this._sampler.executeAsync(
 | 
			
		||||
            this._sampler.getTokenDecimals([makerToken, takerToken]),
 | 
			
		||||
            // Get native order fillable amounts.
 | 
			
		||||
            this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
 | 
			
		||||
            // Get ETH -> maker token price.
 | 
			
		||||
            this._sampler.getMedianSellRate(
 | 
			
		||||
                feeSourceFilters.sources,
 | 
			
		||||
                makerToken,
 | 
			
		||||
                this._nativeFeeToken,
 | 
			
		||||
                this._nativeFeeTokenAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            // Get ETH -> taker token price.
 | 
			
		||||
            this._sampler.getMedianSellRate(
 | 
			
		||||
                feeSourceFilters.sources,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                this._nativeFeeToken,
 | 
			
		||||
                this._nativeFeeTokenAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            // Get sell quotes for taker -> maker.
 | 
			
		||||
            this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
 | 
			
		||||
            this._sampler.getTwoHopSellQuotes(
 | 
			
		||||
                quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
 | 
			
		||||
                makerToken,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                takerAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            this._sampler.isAddressContract(txOrigin),
 | 
			
		||||
        const samplerPromise = this._sampler.executeBatchAsync(
 | 
			
		||||
            [
 | 
			
		||||
                this._sampler.getTokenDecimals([makerToken, takerToken]),
 | 
			
		||||
                // Get native order fillable amounts.
 | 
			
		||||
                this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
 | 
			
		||||
                // Get ETH -> maker token price.
 | 
			
		||||
                this._sampler.getMedianSellRate(
 | 
			
		||||
                    feeSourceFilters.sources,
 | 
			
		||||
                    makerToken,
 | 
			
		||||
                    this._nativeFeeToken,
 | 
			
		||||
                    this._nativeFeeTokenAmount,
 | 
			
		||||
                ),
 | 
			
		||||
                // Get ETH -> taker token price.
 | 
			
		||||
                this._sampler.getMedianSellRate(
 | 
			
		||||
                    feeSourceFilters.sources,
 | 
			
		||||
                    takerToken,
 | 
			
		||||
                    this._nativeFeeToken,
 | 
			
		||||
                    this._nativeFeeTokenAmount,
 | 
			
		||||
                ),
 | 
			
		||||
                // Get sell quotes for taker -> maker.
 | 
			
		||||
                this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
 | 
			
		||||
                this._sampler.isAddressContract(txOrigin),
 | 
			
		||||
            ],
 | 
			
		||||
            {
 | 
			
		||||
                overrides,
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        // Perform the MultiHop sell quotes in a separate request
 | 
			
		||||
        const multiHopsamplerPromise = this._sampler.executeBatchAsync(
 | 
			
		||||
            [
 | 
			
		||||
                this._sampler.getTwoHopSellQuotes(
 | 
			
		||||
                    quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
 | 
			
		||||
                    makerToken,
 | 
			
		||||
                    takerToken,
 | 
			
		||||
                    takerAmount,
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            {
 | 
			
		||||
                overrides,
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Refresh the cached pools asynchronously if required
 | 
			
		||||
@@ -171,14 +198,15 @@ export class MarketOperationUtils {
 | 
			
		||||
                outputAmountPerEth,
 | 
			
		||||
                inputAmountPerEth,
 | 
			
		||||
                dexQuotes,
 | 
			
		||||
                rawTwoHopQuotes,
 | 
			
		||||
                isTxOriginContract,
 | 
			
		||||
            ],
 | 
			
		||||
        ] = await Promise.all([samplerPromise]);
 | 
			
		||||
            [rawTwoHopQuotes],
 | 
			
		||||
        ] = await Promise.all([samplerPromise, multiHopsamplerPromise]);
 | 
			
		||||
 | 
			
		||||
        // Filter out any invalid two hop quotes where we couldn't find a route
 | 
			
		||||
        const twoHopQuotes = rawTwoHopQuotes.filter(
 | 
			
		||||
            q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
 | 
			
		||||
            (q: DexSample<MultiHopFillData>) =>
 | 
			
		||||
                q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
 | 
			
		||||
@@ -232,35 +260,42 @@ export class MarketOperationUtils {
 | 
			
		||||
        // Used to determine whether the tx origin is an EOA or a contract
 | 
			
		||||
        const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
 | 
			
		||||
 | 
			
		||||
        const { overrides } = await this._fetchTokenOverridesAsync(takerToken, makerToken);
 | 
			
		||||
        // Call the sampler contract.
 | 
			
		||||
        const samplerPromise = this._sampler.executeAsync(
 | 
			
		||||
            this._sampler.getTokenDecimals([makerToken, takerToken]),
 | 
			
		||||
            // Get native order fillable amounts.
 | 
			
		||||
            this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
 | 
			
		||||
            // Get ETH -> makerToken token price.
 | 
			
		||||
            this._sampler.getMedianSellRate(
 | 
			
		||||
                feeSourceFilters.sources,
 | 
			
		||||
                makerToken,
 | 
			
		||||
                this._nativeFeeToken,
 | 
			
		||||
                this._nativeFeeTokenAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            // Get ETH -> taker token price.
 | 
			
		||||
            this._sampler.getMedianSellRate(
 | 
			
		||||
                feeSourceFilters.sources,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                this._nativeFeeToken,
 | 
			
		||||
                this._nativeFeeTokenAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            // Get buy quotes for taker -> maker.
 | 
			
		||||
            this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
 | 
			
		||||
        const samplerPromise = this._sampler.executeBatchAsync(
 | 
			
		||||
            [
 | 
			
		||||
                this._sampler.getTokenDecimals([makerToken, takerToken]),
 | 
			
		||||
                // Get native order fillable amounts.
 | 
			
		||||
                this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
 | 
			
		||||
                // Get ETH -> makerToken token price.
 | 
			
		||||
                this._sampler.getMedianSellRate(
 | 
			
		||||
                    feeSourceFilters.sources,
 | 
			
		||||
                    makerToken,
 | 
			
		||||
                    this._nativeFeeToken,
 | 
			
		||||
                    this._nativeFeeTokenAmount,
 | 
			
		||||
                ),
 | 
			
		||||
                // Get ETH -> taker token price.
 | 
			
		||||
                this._sampler.getMedianSellRate(
 | 
			
		||||
                    feeSourceFilters.sources,
 | 
			
		||||
                    takerToken,
 | 
			
		||||
                    this._nativeFeeToken,
 | 
			
		||||
                    this._nativeFeeTokenAmount,
 | 
			
		||||
                ),
 | 
			
		||||
                // Get buy quotes for taker -> maker.
 | 
			
		||||
                this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
 | 
			
		||||
                this._sampler.isAddressContract(txOrigin),
 | 
			
		||||
            ],
 | 
			
		||||
            { overrides },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const multiHopPromise = this._sampler.executeBatchAsync([
 | 
			
		||||
            this._sampler.getTwoHopBuyQuotes(
 | 
			
		||||
                quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
 | 
			
		||||
                makerToken,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                makerAmount,
 | 
			
		||||
            ),
 | 
			
		||||
            this._sampler.isAddressContract(txOrigin),
 | 
			
		||||
        );
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // Refresh the cached pools asynchronously if required
 | 
			
		||||
        void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
 | 
			
		||||
@@ -272,14 +307,15 @@ export class MarketOperationUtils {
 | 
			
		||||
                ethToMakerAssetRate,
 | 
			
		||||
                ethToTakerAssetRate,
 | 
			
		||||
                dexQuotes,
 | 
			
		||||
                rawTwoHopQuotes,
 | 
			
		||||
                isTxOriginContract,
 | 
			
		||||
            ],
 | 
			
		||||
        ] = await Promise.all([samplerPromise]);
 | 
			
		||||
            [rawTwoHopQuotes],
 | 
			
		||||
        ] = await Promise.all([samplerPromise, multiHopPromise]);
 | 
			
		||||
 | 
			
		||||
        // Filter out any invalid two hop quotes where we couldn't find a route
 | 
			
		||||
        const twoHopQuotes = rawTwoHopQuotes.filter(
 | 
			
		||||
            q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
 | 
			
		||||
            (q: DexSample<MultiHopFillData>) =>
 | 
			
		||||
                q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
 | 
			
		||||
@@ -324,17 +360,17 @@ export class MarketOperationUtils {
 | 
			
		||||
    public async getBatchMarketBuyOrdersAsync(
 | 
			
		||||
        batchNativeOrders: SignedNativeOrder[][],
 | 
			
		||||
        makerAmounts: BigNumber[],
 | 
			
		||||
        opts?: Partial<GetMarketOrdersOpts>,
 | 
			
		||||
        gasPrice: BigNumber,
 | 
			
		||||
        opts: GetMarketOrdersOpts,
 | 
			
		||||
    ): Promise<Array<OptimizerResult | undefined>> {
 | 
			
		||||
        if (batchNativeOrders.length === 0) {
 | 
			
		||||
            throw new Error(AggregationError.EmptyOrders);
 | 
			
		||||
        }
 | 
			
		||||
        const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
 | 
			
		||||
 | 
			
		||||
        const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
 | 
			
		||||
        const requestFilters = new SourceFilters().exclude(opts.excludedSources).include(opts.includedSources);
 | 
			
		||||
        const quoteSourceFilters = this._buySources.merge(requestFilters);
 | 
			
		||||
 | 
			
		||||
        const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
 | 
			
		||||
        const feeSourceFilters = this._feeSources.exclude(opts.excludedFeeSources);
 | 
			
		||||
 | 
			
		||||
        const ops = [
 | 
			
		||||
            ...batchNativeOrders.map(orders =>
 | 
			
		||||
@@ -402,11 +438,11 @@ export class MarketOperationUtils {
 | 
			
		||||
                            isRfqSupported: false,
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            bridgeSlippage: _opts.bridgeSlippage,
 | 
			
		||||
                            maxFallbackSlippage: _opts.maxFallbackSlippage,
 | 
			
		||||
                            excludedSources: _opts.excludedSources,
 | 
			
		||||
                            feeSchedule: _opts.feeSchedule,
 | 
			
		||||
                            allowFallback: _opts.allowFallback,
 | 
			
		||||
                            bridgeSlippage: opts.bridgeSlippage,
 | 
			
		||||
                            maxFallbackSlippage: opts.maxFallbackSlippage,
 | 
			
		||||
                            excludedSources: opts.excludedSources,
 | 
			
		||||
                            allowFallback: opts.allowFallback,
 | 
			
		||||
                            gasPrice,
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                    return optimizerResult;
 | 
			
		||||
@@ -466,7 +502,7 @@ export class MarketOperationUtils {
 | 
			
		||||
            outputAmountPerEth,
 | 
			
		||||
            inputAmountPerEth,
 | 
			
		||||
            excludedSources: opts.excludedSources,
 | 
			
		||||
            feeSchedule: opts.feeSchedule,
 | 
			
		||||
            gasPrice: opts.gasPrice,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Find the optimal path.
 | 
			
		||||
@@ -490,7 +526,7 @@ export class MarketOperationUtils {
 | 
			
		||||
 | 
			
		||||
        const { adjustedRate: bestTwoHopRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
 | 
			
		||||
            marketSideLiquidity,
 | 
			
		||||
            opts.feeSchedule,
 | 
			
		||||
            opts.gasPrice,
 | 
			
		||||
            opts.exchangeProxyOverhead,
 | 
			
		||||
        );
 | 
			
		||||
        if (bestTwoHopQuote && bestTwoHopRate.isGreaterThan(optimalPathRate)) {
 | 
			
		||||
@@ -523,7 +559,7 @@ export class MarketOperationUtils {
 | 
			
		||||
            const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
 | 
			
		||||
            const sturdyOptimalPath = await findOptimalPathAsync(side, sturdyFills, inputAmount, opts.runLimit, {
 | 
			
		||||
                ...penaltyOpts,
 | 
			
		||||
                exchangeProxyOverhead: (sourceFlags: number) =>
 | 
			
		||||
                exchangeProxyOverhead: (sourceFlags: bigint) =>
 | 
			
		||||
                    // tslint:disable-next-line: no-bitwise
 | 
			
		||||
                    penaltyOpts.exchangeProxyOverhead(sourceFlags | optimalPath.sourceFlags),
 | 
			
		||||
            });
 | 
			
		||||
@@ -558,16 +594,15 @@ export class MarketOperationUtils {
 | 
			
		||||
        nativeOrders: SignedNativeOrder[],
 | 
			
		||||
        amount: BigNumber,
 | 
			
		||||
        side: MarketOperation,
 | 
			
		||||
        opts?: Partial<GetMarketOrdersOpts>,
 | 
			
		||||
        opts: GetMarketOrdersOpts,
 | 
			
		||||
    ): Promise<OptimizerResultWithReport> {
 | 
			
		||||
        const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
 | 
			
		||||
        const optimizerOpts: GenerateOptimizedOrdersOpts = {
 | 
			
		||||
            bridgeSlippage: _opts.bridgeSlippage,
 | 
			
		||||
            maxFallbackSlippage: _opts.maxFallbackSlippage,
 | 
			
		||||
            excludedSources: _opts.excludedSources,
 | 
			
		||||
            feeSchedule: _opts.feeSchedule,
 | 
			
		||||
            allowFallback: _opts.allowFallback,
 | 
			
		||||
            exchangeProxyOverhead: _opts.exchangeProxyOverhead,
 | 
			
		||||
            bridgeSlippage: opts.bridgeSlippage,
 | 
			
		||||
            maxFallbackSlippage: opts.maxFallbackSlippage,
 | 
			
		||||
            excludedSources: opts.excludedSources,
 | 
			
		||||
            allowFallback: opts.allowFallback,
 | 
			
		||||
            exchangeProxyOverhead: opts.exchangeProxyOverhead,
 | 
			
		||||
            gasPrice: opts.gasPrice,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (nativeOrders.length === 0) {
 | 
			
		||||
@@ -579,7 +614,7 @@ export class MarketOperationUtils {
 | 
			
		||||
            side === MarketOperation.Sell
 | 
			
		||||
                ? this.getMarketSellLiquidityAsync.bind(this)
 | 
			
		||||
                : this.getMarketBuyLiquidityAsync.bind(this);
 | 
			
		||||
        const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
 | 
			
		||||
        const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, opts);
 | 
			
		||||
        let optimizerResult: OptimizerResult | undefined;
 | 
			
		||||
        try {
 | 
			
		||||
            optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
 | 
			
		||||
@@ -600,13 +635,13 @@ export class MarketOperationUtils {
 | 
			
		||||
                optimizerResult.adjustedRate,
 | 
			
		||||
                amount,
 | 
			
		||||
                marketSideLiquidity,
 | 
			
		||||
                _opts.feeSchedule,
 | 
			
		||||
                _opts.exchangeProxyOverhead,
 | 
			
		||||
                opts.gasPrice,
 | 
			
		||||
                opts.exchangeProxyOverhead,
 | 
			
		||||
            ).wholeOrder;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If RFQ liquidity is enabled, make a request to check RFQ liquidity against the first optimizer result
 | 
			
		||||
        const { rfqt } = _opts;
 | 
			
		||||
        const { rfqt } = opts;
 | 
			
		||||
        if (
 | 
			
		||||
            marketSideLiquidity.isRfqSupported &&
 | 
			
		||||
            rfqt &&
 | 
			
		||||
@@ -697,9 +732,9 @@ export class MarketOperationUtils {
 | 
			
		||||
 | 
			
		||||
        // Compute Quote Report and return the results.
 | 
			
		||||
        let quoteReport: QuoteReport | undefined;
 | 
			
		||||
        if (_opts.shouldGenerateQuoteReport) {
 | 
			
		||||
        if (opts.shouldGenerateQuoteReport) {
 | 
			
		||||
            quoteReport = MarketOperationUtils._computeQuoteReport(
 | 
			
		||||
                _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
 | 
			
		||||
                opts.rfqt ? opts.rfqt.quoteRequestor : undefined,
 | 
			
		||||
                marketSideLiquidity,
 | 
			
		||||
                optimizerResult,
 | 
			
		||||
                wholeOrderPrice,
 | 
			
		||||
@@ -707,9 +742,9 @@ export class MarketOperationUtils {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let priceComparisonsReport: PriceComparisonsReport | undefined;
 | 
			
		||||
        if (_opts.shouldIncludePriceComparisonsReport) {
 | 
			
		||||
        if (opts.shouldIncludePriceComparisonsReport) {
 | 
			
		||||
            priceComparisonsReport = MarketOperationUtils._computePriceComparisonsReport(
 | 
			
		||||
                _opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
 | 
			
		||||
                opts.rfqt ? opts.rfqt.quoteRequestor : undefined,
 | 
			
		||||
                marketSideLiquidity,
 | 
			
		||||
                wholeOrderPrice,
 | 
			
		||||
            );
 | 
			
		||||
@@ -727,6 +762,61 @@ export class MarketOperationUtils {
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async _fetchTokenOverridesAsync(
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
    ): Promise<{ overrides: { [address: string]: { code: string } } }> {
 | 
			
		||||
        const overrides: { [address: string]: { code: string } } = {};
 | 
			
		||||
        // Set the gas overhead counter to a known address
 | 
			
		||||
        overrides[GAS_OVERHEAD_ADDRESS] = { code: GAS_OVERHEAD_BYTECODE };
 | 
			
		||||
        // Set the fixed impl of a HackedERC20 bytecode, all other tokens use a delegate call
 | 
			
		||||
        overrides[HACKED_ERC20_ADDRESS] = { code: HACKED_ERC20_BYTECODE };
 | 
			
		||||
 | 
			
		||||
        if (!this._sampler.tokenAdjacencyGraph) {
 | 
			
		||||
            return { overrides };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Allow the tokens bytecode to be overwritten using geths override functionality
 | 
			
		||||
        const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this._sampler.tokenAdjacencyGraph);
 | 
			
		||||
        const tokens = [takerToken, makerToken, ...intermediateTokens].map(t => t.toLowerCase());
 | 
			
		||||
 | 
			
		||||
        // Fetch all of the missing token codes
 | 
			
		||||
        const missingTokenCodesTokens = tokens.filter(t => !this._contractCodeByAddress[t]);
 | 
			
		||||
        if (missingTokenCodesTokens.length > 0) {
 | 
			
		||||
            const missingTokenCodes = await this._sampler.executeBatchAsync(
 | 
			
		||||
                missingTokenCodesTokens.map(t => this._sampler.getCode(t)),
 | 
			
		||||
            );
 | 
			
		||||
            missingTokenCodes.forEach((code, i) => {
 | 
			
		||||
                this._contractCodeByAddress[missingTokenCodesTokens[i]] = code;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const tokenCodes = tokens.map(t => this._contractCodeByAddress[t]!);
 | 
			
		||||
 | 
			
		||||
        const nativeWrappedToken = NATIVE_FEE_TOKEN_BY_CHAIN_ID[this._sampler.chainId];
 | 
			
		||||
        tokens.forEach((token, i) => {
 | 
			
		||||
            // Skip overriding WETH like token as this can be used directly with a deposit
 | 
			
		||||
            if (token === nativeWrappedToken) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const tokenImplAddress = ethjs.bufferToHex(
 | 
			
		||||
                ethjs.setLengthLeft(
 | 
			
		||||
                    // tslint:disable-next-line: custom-no-magic-numbers prefer-template
 | 
			
		||||
                    ethjs.toBuffer(`0x${new BigNumber(token.toLowerCase()).plus(1).toString(16)}`),
 | 
			
		||||
                    // tslint:disable-next-line: custom-no-magic-numbers prefer-template
 | 
			
		||||
                    20,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            // Override the original implementation with the HackedERC20 delegate
 | 
			
		||||
            overrides[token] = { code: DELEGEATE_HACKED_ERC20_BYTECODE };
 | 
			
		||||
            // Specify the original implementation at a new address (+1)
 | 
			
		||||
            overrides[tokenImplAddress] = { code: tokenCodes[i] };
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return { overrides };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tslint:disable: max-file-line-count
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,181 @@
 | 
			
		||||
import { ContractFunctionObj } from '@0x/base-contract';
 | 
			
		||||
import { BigNumber, decodeBytesAsRevertError, logUtils } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
 | 
			
		||||
 | 
			
		||||
import { ERC20BridgeSource, FillData, MeasuredSamplerResult, MeasuredSourceQuoteOperation } from './types';
 | 
			
		||||
 | 
			
		||||
export type Parameters<T> = T extends (...args: infer TArgs) => any ? TArgs : never;
 | 
			
		||||
 | 
			
		||||
export interface MeasuredSamplerContractCall<
 | 
			
		||||
    TFunc extends (...args: any[]) => ContractFunctionObj<any>,
 | 
			
		||||
    TFillData extends FillData = FillData
 | 
			
		||||
> {
 | 
			
		||||
    contract: ERC20BridgeSamplerContract;
 | 
			
		||||
    function: TFunc;
 | 
			
		||||
    params: Parameters<TFunc>;
 | 
			
		||||
    callback?: (callResults: string, fillData: TFillData) => MeasuredSamplerResult;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PathDeregister {
 | 
			
		||||
    private static _instance: PathDeregister;
 | 
			
		||||
    // Presence in this registry with a negtive number indicates the Path has been deregistered
 | 
			
		||||
    private readonly _registry: { [key in ERC20BridgeSource]?: { [key: string]: number } } = {};
 | 
			
		||||
    private readonly _MAX_RESULTS = 100;
 | 
			
		||||
 | 
			
		||||
    public static createKey(args: any[]): string {
 | 
			
		||||
        return args
 | 
			
		||||
            .map(a => {
 | 
			
		||||
                if (typeof a === 'object' && a !== null) {
 | 
			
		||||
                    return Object.values(a).join('-');
 | 
			
		||||
                }
 | 
			
		||||
                if (Array.isArray(a)) {
 | 
			
		||||
                    return a.join('-');
 | 
			
		||||
                }
 | 
			
		||||
                return a.toString();
 | 
			
		||||
            })
 | 
			
		||||
            .join('-');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static getInstance(): PathDeregister {
 | 
			
		||||
        if (!PathDeregister._instance) {
 | 
			
		||||
            PathDeregister._instance = new PathDeregister();
 | 
			
		||||
        }
 | 
			
		||||
        return PathDeregister._instance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static _getRandom(): number {
 | 
			
		||||
        // tslint:disable-next-line: custom-no-magic-numbers
 | 
			
		||||
        return Math.floor(Math.random() * (100 - 0 + 1)) + 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public isDeregistered(source: ERC20BridgeSource, key: string): boolean {
 | 
			
		||||
        if (!this._registry[source]) {
 | 
			
		||||
            this._registry[source] = {};
 | 
			
		||||
        }
 | 
			
		||||
        // Randomly allow the ops to be re-registered
 | 
			
		||||
        if (PathDeregister._getRandom() === 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return this._registry[source]![key] < 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Registers a successful result. Upon having one single success
 | 
			
		||||
    // a Path is no longer deregistered
 | 
			
		||||
    public handleResult(source: ERC20BridgeSource, key: string, result: MeasuredSamplerResult): void {
 | 
			
		||||
        if (!this._registry[source]) {
 | 
			
		||||
            this._registry[source] = {};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Defaults to 0
 | 
			
		||||
        if (!this._registry[source]![key]) {
 | 
			
		||||
            this._registry[source]![key] = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._didSucceed(result)) {
 | 
			
		||||
            if (this._registry[source]![key] < 0) {
 | 
			
		||||
                this._registry[source]![key] = 0;
 | 
			
		||||
            }
 | 
			
		||||
            this._registry[source]![key] = Math.min(this._MAX_RESULTS, this._registry[source]![key] + 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._registry[source]![key] = Math.max(-this._MAX_RESULTS, this._registry[source]![key] - 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // tslint:disable-next-line: prefer-function-over-method
 | 
			
		||||
    private _didSucceed(result: MeasuredSamplerResult): boolean {
 | 
			
		||||
        const nonZeroSample = result.samples.find(s => s.isGreaterThan(0));
 | 
			
		||||
        return nonZeroSample !== undefined && result.samples.length > 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tslint:disable-next-line: max-classes-per-file
 | 
			
		||||
export class MeasuredSamplerContractOperation<
 | 
			
		||||
    TFunc extends (...args: any[]) => ContractFunctionObj<any>,
 | 
			
		||||
    TFillData extends FillData = FillData
 | 
			
		||||
> implements MeasuredSourceQuoteOperation<TFillData> {
 | 
			
		||||
    public readonly source: ERC20BridgeSource;
 | 
			
		||||
    public fillData: TFillData;
 | 
			
		||||
    private readonly _samplerContract: ERC20BridgeSamplerContract;
 | 
			
		||||
    private readonly _samplerFunction: TFunc;
 | 
			
		||||
    private readonly _params: Parameters<TFunc>;
 | 
			
		||||
    private readonly _callback?: (callResults: string, fillData: TFillData) => MeasuredSamplerResult;
 | 
			
		||||
    private readonly _deregisterKey: string | undefined;
 | 
			
		||||
    private readonly _deregisterable: boolean;
 | 
			
		||||
    private readonly _log: boolean;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        opts: {
 | 
			
		||||
            source: ERC20BridgeSource;
 | 
			
		||||
            fillData?: TFillData;
 | 
			
		||||
            deregisterable?: boolean;
 | 
			
		||||
            log?: boolean;
 | 
			
		||||
        } & MeasuredSamplerContractCall<TFunc, TFillData>,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.source = opts.source;
 | 
			
		||||
        this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion
 | 
			
		||||
        this._samplerContract = opts.contract;
 | 
			
		||||
        this._samplerFunction = opts.function;
 | 
			
		||||
        this._params = opts.params;
 | 
			
		||||
        this._callback = opts.callback;
 | 
			
		||||
        this._deregisterable = opts.deregisterable || false;
 | 
			
		||||
        this._log = opts.log || false;
 | 
			
		||||
        if (this._deregisterable) {
 | 
			
		||||
            this._deregisterKey = PathDeregister.createKey(this._params.slice(0, this._params.length - 1));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public encodeCall(): string {
 | 
			
		||||
        return this._samplerFunction
 | 
			
		||||
            .bind(this._samplerContract)(...this._params)
 | 
			
		||||
            .getABIEncodedTransactionData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public handleCallResults(callResults: string): MeasuredSamplerResult {
 | 
			
		||||
        let result: MeasuredSamplerResult;
 | 
			
		||||
        if (this._callback !== undefined) {
 | 
			
		||||
            result = this._callback(callResults, this.fillData);
 | 
			
		||||
        } else {
 | 
			
		||||
            const [gasUsed, samples] = this._samplerContract.getABIDecodedReturnData<[BigNumber[], BigNumber[]]>(
 | 
			
		||||
                this._samplerFunction.name,
 | 
			
		||||
                callResults,
 | 
			
		||||
            );
 | 
			
		||||
            result = { gasUsed, samples };
 | 
			
		||||
        }
 | 
			
		||||
        if (this._deregisterKey) {
 | 
			
		||||
            PathDeregister.getInstance().handleResult(this.source, this._deregisterKey, result);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._log) {
 | 
			
		||||
            logUtils.log({ source: this.source, fillData: this.fillData, ...result });
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public handleRevert(callResults: string): MeasuredSamplerResult {
 | 
			
		||||
        let msg = callResults;
 | 
			
		||||
        try {
 | 
			
		||||
            msg = decodeBytesAsRevertError(callResults).toString();
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // do nothing
 | 
			
		||||
        }
 | 
			
		||||
        logUtils.warn(
 | 
			
		||||
            `SamplerContractOperation: ${this.source}.${this._samplerFunction.name} reverted ${msg} ${JSON.stringify(
 | 
			
		||||
                this.fillData,
 | 
			
		||||
                null,
 | 
			
		||||
                2,
 | 
			
		||||
            )}`,
 | 
			
		||||
        );
 | 
			
		||||
        const result = { gasUsed: [], samples: [] };
 | 
			
		||||
        if (this._deregisterKey) {
 | 
			
		||||
            PathDeregister.getInstance().handleResult(this.source, this._deregisterKey, result);
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public isDeregistered(): boolean {
 | 
			
		||||
        if (this._deregisterKey) {
 | 
			
		||||
            return PathDeregister.getInstance().isDeregistered(this.source, this._deregisterKey);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,14 +5,7 @@ import { Omit } from '../../types';
 | 
			
		||||
 | 
			
		||||
import { ZERO_AMOUNT } from './constants';
 | 
			
		||||
import { getTwoHopAdjustedRate } from './rate_utils';
 | 
			
		||||
import {
 | 
			
		||||
    DexSample,
 | 
			
		||||
    ExchangeProxyOverhead,
 | 
			
		||||
    FeeSchedule,
 | 
			
		||||
    MarketSideLiquidity,
 | 
			
		||||
    MultiHopFillData,
 | 
			
		||||
    TokenAdjacencyGraph,
 | 
			
		||||
} from './types';
 | 
			
		||||
import { DexSample, ExchangeProxyOverhead, MarketSideLiquidity, MultiHopFillData, TokenAdjacencyGraph } from './types';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given a token pair, returns the intermediate tokens to consider for two-hop routes.
 | 
			
		||||
@@ -36,7 +29,7 @@ export function getIntermediateTokens(
 | 
			
		||||
 */
 | 
			
		||||
export function getBestTwoHopQuote(
 | 
			
		||||
    marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
 | 
			
		||||
    feeSchedule?: FeeSchedule,
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
    exchangeProxyOverhead?: ExchangeProxyOverhead,
 | 
			
		||||
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
 | 
			
		||||
    const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
 | 
			
		||||
@@ -57,7 +50,7 @@ export function getBestTwoHopQuote(
 | 
			
		||||
    }
 | 
			
		||||
    const best = filteredQuotes
 | 
			
		||||
        .map(quote =>
 | 
			
		||||
            getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
 | 
			
		||||
            getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, gasPrice, exchangeProxyOverhead),
 | 
			
		||||
        )
 | 
			
		||||
        .reduce(
 | 
			
		||||
            (prev, curr, i) =>
 | 
			
		||||
@@ -68,7 +61,7 @@ export function getBestTwoHopQuote(
 | 
			
		||||
                    filteredQuotes[0],
 | 
			
		||||
                    inputAmount,
 | 
			
		||||
                    outputAmountPerEth,
 | 
			
		||||
                    feeSchedule,
 | 
			
		||||
                    gasPrice,
 | 
			
		||||
                    exchangeProxyOverhead,
 | 
			
		||||
                ),
 | 
			
		||||
                quote: filteredQuotes[0],
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import { AbiEncoder, BigNumber } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
 | 
			
		||||
 | 
			
		||||
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
 | 
			
		||||
import { MAX_UINT256, NATIVE_LIMIT_ORDER_GAS_USED, NATIVE_RFQT_GAS_USED, ZERO_AMOUNT } from './constants';
 | 
			
		||||
import {
 | 
			
		||||
    AggregationError,
 | 
			
		||||
    BalancerFillData,
 | 
			
		||||
@@ -61,6 +61,7 @@ export function createOrdersFromTwoHopSample(
 | 
			
		||||
        output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
 | 
			
		||||
        subFills: [],
 | 
			
		||||
        fillData: firstHopSource.fillData,
 | 
			
		||||
        gasUsed: ZERO_AMOUNT,
 | 
			
		||||
    };
 | 
			
		||||
    const secondHopFill: CollapsedFill = {
 | 
			
		||||
        sourcePathId: '',
 | 
			
		||||
@@ -70,6 +71,7 @@ export function createOrdersFromTwoHopSample(
 | 
			
		||||
        output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
 | 
			
		||||
        subFills: [],
 | 
			
		||||
        fillData: secondHopSource.fillData,
 | 
			
		||||
        gasUsed: sample.gasUsed,
 | 
			
		||||
    };
 | 
			
		||||
    return [
 | 
			
		||||
        createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
 | 
			
		||||
@@ -168,6 +170,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
 | 
			
		||||
            return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'WaultSwap');
 | 
			
		||||
        case ERC20BridgeSource.Polydex:
 | 
			
		||||
            return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Polydex');
 | 
			
		||||
        case ERC20BridgeSource.FirebirdOneSwap:
 | 
			
		||||
            return encodeBridgeSourceId(BridgeProtocol.Nerve, 'FirebirdOneSwap');
 | 
			
		||||
        case ERC20BridgeSource.Lido:
 | 
			
		||||
            return encodeBridgeSourceId(BridgeProtocol.Lido, 'Lido');
 | 
			
		||||
        default:
 | 
			
		||||
@@ -201,6 +205,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
 | 
			
		||||
        case ERC20BridgeSource.Smoothy:
 | 
			
		||||
        case ERC20BridgeSource.Saddle:
 | 
			
		||||
        case ERC20BridgeSource.XSigma:
 | 
			
		||||
        case ERC20BridgeSource.FirebirdOneSwap:
 | 
			
		||||
            const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
 | 
			
		||||
            bridgeData = encoder.encode([
 | 
			
		||||
                curveFillData.pool.poolAddress,
 | 
			
		||||
@@ -326,6 +331,7 @@ export function createBridgeOrder(
 | 
			
		||||
        sourcePathId: fill.sourcePathId,
 | 
			
		||||
        type: FillQuoteTransformerOrderType.Bridge,
 | 
			
		||||
        fills: [fill],
 | 
			
		||||
        gasUsed: fill.gasUsed,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -417,6 +423,7 @@ export const BRIDGE_ENCODERS: {
 | 
			
		||||
    [ERC20BridgeSource.Smoothy]: curveEncoder,
 | 
			
		||||
    [ERC20BridgeSource.Saddle]: curveEncoder,
 | 
			
		||||
    [ERC20BridgeSource.XSigma]: curveEncoder,
 | 
			
		||||
    [ERC20BridgeSource.FirebirdOneSwap]: curveEncoder,
 | 
			
		||||
    // UniswapV2 like, (router, address[])
 | 
			
		||||
    [ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
 | 
			
		||||
    [ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
 | 
			
		||||
@@ -483,6 +490,16 @@ export function createNativeOptimizedOrder(
 | 
			
		||||
        fillData,
 | 
			
		||||
    };
 | 
			
		||||
    return fill.type === FillQuoteTransformerOrderType.Rfq
 | 
			
		||||
        ? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData }
 | 
			
		||||
        : { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData };
 | 
			
		||||
        ? {
 | 
			
		||||
              ...base,
 | 
			
		||||
              type: FillQuoteTransformerOrderType.Rfq,
 | 
			
		||||
              fillData: fillData as NativeRfqOrderFillData,
 | 
			
		||||
              gasUsed: NATIVE_RFQT_GAS_USED,
 | 
			
		||||
          }
 | 
			
		||||
        : {
 | 
			
		||||
              ...base,
 | 
			
		||||
              type: FillQuoteTransformerOrderType.Limit,
 | 
			
		||||
              fillData: fillData as NativeLimitOrderFillData,
 | 
			
		||||
              gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
          };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = {
 | 
			
		||||
export class Path {
 | 
			
		||||
    public collapsedFills?: ReadonlyArray<CollapsedFill>;
 | 
			
		||||
    public orders?: OptimizedMarketOrder[];
 | 
			
		||||
    public sourceFlags: number = 0;
 | 
			
		||||
    public sourceFlags: bigint = BigInt(0);
 | 
			
		||||
    protected _size: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT };
 | 
			
		||||
    protected _adjustedSize: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT };
 | 
			
		||||
 | 
			
		||||
@@ -95,7 +95,7 @@ export class Path {
 | 
			
		||||
        const nativeFills = this.fills.filter(f => f.source === ERC20BridgeSource.Native);
 | 
			
		||||
        this.fills = [...nativeFills.filter(f => f !== lastNativeFillIfExists), ...fallback.fills];
 | 
			
		||||
        // Recompute the source flags
 | 
			
		||||
        this.sourceFlags = this.fills.reduce((flags, fill) => flags | fill.flags, 0);
 | 
			
		||||
        this.sourceFlags = this.fills.reduce((flags, fill) => flags | fill.flags, BigInt(0));
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -241,6 +241,8 @@ export class Path {
 | 
			
		||||
                    prevFill.input = prevFill.input.plus(fill.input);
 | 
			
		||||
                    prevFill.output = prevFill.output.plus(fill.output);
 | 
			
		||||
                    prevFill.fillData = fill.fillData;
 | 
			
		||||
                    // Gas Used is always increasing
 | 
			
		||||
                    prevFill.gasUsed = fill.gasUsed;
 | 
			
		||||
                    prevFill.subFills.push(fill);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
@@ -252,6 +254,7 @@ export class Path {
 | 
			
		||||
                fillData: fill.fillData,
 | 
			
		||||
                input: fill.input,
 | 
			
		||||
                output: fill.output,
 | 
			
		||||
                gasUsed: fill.gasUsed,
 | 
			
		||||
                subFills: [fill],
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { MarketOperation } from '../../types';
 | 
			
		||||
 | 
			
		||||
import { SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
 | 
			
		||||
import { DexSample, ERC20BridgeSource, ExchangeProxyOverhead, FeeSchedule, MultiHopFillData } from './types';
 | 
			
		||||
import { DexSample, ExchangeProxyOverhead, MultiHopFillData } from './types';
 | 
			
		||||
 | 
			
		||||
// tslint:disable:no-bitwise
 | 
			
		||||
 | 
			
		||||
@@ -16,20 +16,19 @@ export function getTwoHopAdjustedRate(
 | 
			
		||||
    twoHopQuote: DexSample<MultiHopFillData>,
 | 
			
		||||
    targetInput: BigNumber,
 | 
			
		||||
    outputAmountPerEth: BigNumber,
 | 
			
		||||
    fees: FeeSchedule = {},
 | 
			
		||||
    gasPrice: BigNumber,
 | 
			
		||||
    exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
 | 
			
		||||
): BigNumber {
 | 
			
		||||
    const { output, input, fillData } = twoHopQuote;
 | 
			
		||||
    if (input.isLessThan(targetInput) || output.isZero()) {
 | 
			
		||||
        return ZERO_AMOUNT;
 | 
			
		||||
    }
 | 
			
		||||
    const penalty = outputAmountPerEth.times(
 | 
			
		||||
        exchangeProxyOverhead(
 | 
			
		||||
            SOURCE_FLAGS.MultiHop |
 | 
			
		||||
                SOURCE_FLAGS[fillData.firstHopSource.source] |
 | 
			
		||||
                SOURCE_FLAGS[fillData.secondHopSource.source],
 | 
			
		||||
        ).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
 | 
			
		||||
    );
 | 
			
		||||
    const costInEth = exchangeProxyOverhead(
 | 
			
		||||
        SOURCE_FLAGS.MultiHop |
 | 
			
		||||
            SOURCE_FLAGS[fillData.firstHopSource.source] |
 | 
			
		||||
            SOURCE_FLAGS[fillData.secondHopSource.source],
 | 
			
		||||
    ).plus(twoHopQuote.gasUsed.times(gasPrice));
 | 
			
		||||
    const penalty = outputAmountPerEth.times(costInEth);
 | 
			
		||||
    const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
 | 
			
		||||
    return side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { ChainId } from '@0x/contract-addresses';
 | 
			
		||||
import { BigNumber, NULL_BYTES } from '@0x/utils';
 | 
			
		||||
import _ = require('lodash');
 | 
			
		||||
 | 
			
		||||
import { SamplerOverrides } from '../../types';
 | 
			
		||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
 | 
			
		||||
@@ -141,11 +142,12 @@ export class DexOrderSampler extends SamplerOperations {
 | 
			
		||||
     * Run a series of operations from `DexOrderSampler.ops` in a single transaction.
 | 
			
		||||
     * Takes an arbitrary length array, but is not typesafe.
 | 
			
		||||
     */
 | 
			
		||||
    public async executeBatchAsync<T extends Array<BatchedOperation<any>>>(ops: T): Promise<any[]> {
 | 
			
		||||
    public async executeBatchAsync<T extends Array<BatchedOperation<any>>>(
 | 
			
		||||
        ops: T,
 | 
			
		||||
        opts: Partial<SamplerOverrides> = {},
 | 
			
		||||
    ): Promise<any[]> {
 | 
			
		||||
        const callDatas = ops.map(o => o.encodeCall());
 | 
			
		||||
        const { overrides, block } = this._samplerOverrides
 | 
			
		||||
            ? this._samplerOverrides
 | 
			
		||||
            : { overrides: undefined, block: undefined };
 | 
			
		||||
        const { overrides, block } = _.merge(opts, this._samplerOverrides);
 | 
			
		||||
 | 
			
		||||
        // All operations are NOOPs
 | 
			
		||||
        if (callDatas.every(cd => cd === NULL_BYTES)) {
 | 
			
		||||
@@ -157,11 +159,12 @@ export class DexOrderSampler extends SamplerOperations {
 | 
			
		||||
            .callAsync({ overrides }, block);
 | 
			
		||||
        // Return the parsed results.
 | 
			
		||||
        let rawCallResultsIdx = 0;
 | 
			
		||||
        return callDatas.map((callData, i) => {
 | 
			
		||||
        const results = callDatas.map((callData, i) => {
 | 
			
		||||
            // tslint:disable-next-line:boolean-naming
 | 
			
		||||
            const { data, success } =
 | 
			
		||||
                callData !== NULL_BYTES ? rawCallResults[rawCallResultsIdx++] : { success: true, data: NULL_BYTES };
 | 
			
		||||
            return success ? ops[i].handleCallResults(data) : ops[i].handleRevert(data);
 | 
			
		||||
        });
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,13 @@ export class SamplerContractOperation<
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // do nothing
 | 
			
		||||
        }
 | 
			
		||||
        logUtils.warn(`SamplerContractOperation: ${this.source}.${this._samplerFunction.name} reverted ${msg}`);
 | 
			
		||||
        logUtils.warn(
 | 
			
		||||
            `SamplerContractOperation: ${this.source}.${this._samplerFunction.name} reverted ${msg} ${JSON.stringify(
 | 
			
		||||
                this.fillData,
 | 
			
		||||
                null,
 | 
			
		||||
                2,
 | 
			
		||||
            )}`,
 | 
			
		||||
        );
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -85,6 +85,7 @@ export enum ERC20BridgeSource {
 | 
			
		||||
    Dfyn = 'Dfyn',
 | 
			
		||||
    WaultSwap = 'WaultSwap',
 | 
			
		||||
    Polydex = 'Polydex',
 | 
			
		||||
    FirebirdOneSwap = 'FirebirdOneSwap',
 | 
			
		||||
}
 | 
			
		||||
export type SourcesWithPoolsCache = ERC20BridgeSource.Balancer | ERC20BridgeSource.BalancerV2 | ERC20BridgeSource.Cream;
 | 
			
		||||
 | 
			
		||||
@@ -96,21 +97,13 @@ export enum CurveFunctionSelectors {
 | 
			
		||||
    None = '0x00000000',
 | 
			
		||||
    exchange = '0x3df02124',
 | 
			
		||||
    exchange_underlying = '0xa6417ed6',
 | 
			
		||||
    get_dy_underlying = '0x07211ef7',
 | 
			
		||||
    get_dx_underlying = '0x0e71d1b9',
 | 
			
		||||
    get_dy = '0x5e0d443f',
 | 
			
		||||
    get_dx = '0x67df02ca',
 | 
			
		||||
    // Curve V2
 | 
			
		||||
    exchange_v2 = '0x5b41b908',
 | 
			
		||||
    exchange_underlying_v2 = '0x65b2489b',
 | 
			
		||||
    get_dy_v2 = '0x556d6e9f',
 | 
			
		||||
    get_dy_underlying_v2 = '0x85f11d1e',
 | 
			
		||||
    // Smoothy
 | 
			
		||||
    swap_uint256 = '0x5673b02d', // swap(uint256,uint256,uint256,uint256)
 | 
			
		||||
    get_swap_amount = '0x45cf2ef6', // getSwapAmount(uint256,uint256,uint256)
 | 
			
		||||
    // Nerve BSC, Saddle Mainnet
 | 
			
		||||
    swap = '0x91695586', // swap(uint8,uint8,uint256,uint256,uint256)
 | 
			
		||||
    calculateSwap = '0xa95b089f', // calculateSwap(uint8,uint8,uint256)
 | 
			
		||||
}
 | 
			
		||||
// tslint:enable: enum-naming
 | 
			
		||||
 | 
			
		||||
@@ -119,8 +112,6 @@ export enum CurveFunctionSelectors {
 | 
			
		||||
 */
 | 
			
		||||
export interface CurveInfo {
 | 
			
		||||
    exchangeFunctionSelector: CurveFunctionSelectors;
 | 
			
		||||
    sellQuoteFunctionSelector: CurveFunctionSelectors;
 | 
			
		||||
    buyQuoteFunctionSelector: CurveFunctionSelectors;
 | 
			
		||||
    poolAddress: string;
 | 
			
		||||
    tokens: string[];
 | 
			
		||||
    metaTokens: string[] | undefined;
 | 
			
		||||
@@ -166,7 +157,9 @@ export interface DexSample<TFillData extends FillData = FillData> {
 | 
			
		||||
    fillData: TFillData;
 | 
			
		||||
    input: BigNumber;
 | 
			
		||||
    output: BigNumber;
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CurveFillData extends FillData {
 | 
			
		||||
    fromTokenIdx: number;
 | 
			
		||||
    toTokenIdx: number;
 | 
			
		||||
@@ -222,8 +215,8 @@ export interface GenericRouterFillData extends FillData {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MultiHopFillData extends FillData {
 | 
			
		||||
    firstHopSource: SourceQuoteOperation;
 | 
			
		||||
    secondHopSource: SourceQuoteOperation;
 | 
			
		||||
    firstHopSource: MeasuredSourceQuoteOperation;
 | 
			
		||||
    secondHopSource: MeasuredSourceQuoteOperation;
 | 
			
		||||
    intermediateToken: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -273,7 +266,7 @@ export interface Fill<TFillData extends FillData = FillData> {
 | 
			
		||||
    // paths that have the same `source` IDs but are distinct (e.g., Curves).
 | 
			
		||||
    sourcePathId: string;
 | 
			
		||||
    // See `SOURCE_FLAGS`.
 | 
			
		||||
    flags: number;
 | 
			
		||||
    flags: bigint;
 | 
			
		||||
    // Input fill amount (taker asset amount in a sell, maker asset amount in a buy).
 | 
			
		||||
    input: BigNumber;
 | 
			
		||||
    // Output fill amount (maker asset amount in a sell, taker asset amount in a buy).
 | 
			
		||||
@@ -284,6 +277,7 @@ export interface Fill<TFillData extends FillData = FillData> {
 | 
			
		||||
    parent?: Fill;
 | 
			
		||||
    // The index of the fill in the original path.
 | 
			
		||||
    index: number;
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -312,6 +306,7 @@ export interface CollapsedFill<TFillData extends FillData = FillData> {
 | 
			
		||||
        input: BigNumber;
 | 
			
		||||
        output: BigNumber;
 | 
			
		||||
    }>;
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -328,6 +323,7 @@ export interface OptimizedMarketOrderBase<TFillData extends FillData = FillData>
 | 
			
		||||
    makerAmount: BigNumber; // The amount we wish to buy from this order, e.g inclusive of any previous partial fill
 | 
			
		||||
    takerAmount: BigNumber; // The amount we wish to fill this for, e.g inclusive of any previous partial fill
 | 
			
		||||
    fills: CollapsedFill[];
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface OptimizedMarketBridgeOrder<TFillData extends FillData = FillData>
 | 
			
		||||
@@ -335,6 +331,7 @@ export interface OptimizedMarketBridgeOrder<TFillData extends FillData = FillDat
 | 
			
		||||
    type: FillQuoteTransformerOrderType.Bridge;
 | 
			
		||||
    fillData: TFillData;
 | 
			
		||||
    sourcePathId: string;
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface OptimizedLimitOrder extends OptimizedMarketOrderBase<NativeLimitOrderFillData> {
 | 
			
		||||
@@ -360,9 +357,7 @@ export interface GetMarketOrdersRfqOpts extends RfqRequestOpts {
 | 
			
		||||
    firmQuoteValidator?: RfqFirmQuoteValidator;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type FeeEstimate = (fillData: FillData) => number | BigNumber;
 | 
			
		||||
export type FeeSchedule = Partial<{ [key in ERC20BridgeSource]: FeeEstimate }>;
 | 
			
		||||
export type ExchangeProxyOverhead = (sourceFlags: number) => BigNumber;
 | 
			
		||||
export type ExchangeProxyOverhead = (sourceFlags: bigint) => BigNumber;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
 | 
			
		||||
@@ -415,13 +410,9 @@ export interface GetMarketOrdersOpts {
 | 
			
		||||
     */
 | 
			
		||||
    sampleDistributionBase: number;
 | 
			
		||||
    /**
 | 
			
		||||
     * Fees for each liquidity source, expressed in gas.
 | 
			
		||||
     * The gas overhead of various execution paths
 | 
			
		||||
     * E.g Uniswap VIP overhead, FlashWallet overhead
 | 
			
		||||
     */
 | 
			
		||||
    feeSchedule: FeeSchedule;
 | 
			
		||||
    /**
 | 
			
		||||
     * Estimated gas consumed by each liquidity source.
 | 
			
		||||
     */
 | 
			
		||||
    gasSchedule: FeeSchedule;
 | 
			
		||||
    exchangeProxyOverhead: ExchangeProxyOverhead;
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to pad the quote with a redundant fallback quote using different
 | 
			
		||||
@@ -446,6 +437,10 @@ export interface GetMarketOrdersOpts {
 | 
			
		||||
     * hopping to. E.g DAI->USDC via an adjacent token WETH
 | 
			
		||||
     */
 | 
			
		||||
    tokenAdjacencyGraph: TokenAdjacencyGraph;
 | 
			
		||||
    /**
 | 
			
		||||
     * The current gas price. Used to adjust pricing by the cost of a DEX
 | 
			
		||||
     */
 | 
			
		||||
    gasPrice: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -457,6 +452,18 @@ export interface BatchedOperation<TResult> {
 | 
			
		||||
    handleRevert(callResults: string): TResult;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MeasuredSamplerResult {
 | 
			
		||||
    gasUsed: BigNumber[];
 | 
			
		||||
    samples: BigNumber[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MeasuredSourceQuoteOperation<TFillData extends FillData = FillData>
 | 
			
		||||
    extends BatchedOperation<MeasuredSamplerResult> {
 | 
			
		||||
    readonly source: ERC20BridgeSource;
 | 
			
		||||
    fillData: TFillData;
 | 
			
		||||
    isDeregistered?: () => boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SourceQuoteOperation<TFillData extends FillData = FillData> extends BatchedOperation<BigNumber[]> {
 | 
			
		||||
    readonly source: ERC20BridgeSource;
 | 
			
		||||
    fillData: TFillData;
 | 
			
		||||
@@ -464,7 +471,7 @@ export interface SourceQuoteOperation<TFillData extends FillData = FillData> ext
 | 
			
		||||
 | 
			
		||||
export interface OptimizerResult {
 | 
			
		||||
    optimizedOrders: OptimizedMarketOrder[];
 | 
			
		||||
    sourceFlags: number;
 | 
			
		||||
    sourceFlags: bigint;
 | 
			
		||||
    liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
 | 
			
		||||
    marketSideLiquidity: MarketSideLiquidity;
 | 
			
		||||
    adjustedRate: BigNumber;
 | 
			
		||||
@@ -525,10 +532,10 @@ export interface GenerateOptimizedOrdersOpts {
 | 
			
		||||
    bridgeSlippage?: number;
 | 
			
		||||
    maxFallbackSlippage?: number;
 | 
			
		||||
    excludedSources?: ERC20BridgeSource[];
 | 
			
		||||
    feeSchedule?: FeeSchedule;
 | 
			
		||||
    exchangeProxyOverhead?: ExchangeProxyOverhead;
 | 
			
		||||
    allowFallback?: boolean;
 | 
			
		||||
    shouldBatchBridgeOrders?: boolean;
 | 
			
		||||
    gasPrice: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ComparisonPrice {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import _ = require('lodash');
 | 
			
		||||
 | 
			
		||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../types';
 | 
			
		||||
 | 
			
		||||
import { NATIVE_LIMIT_ORDER_GAS_USED, NATIVE_RFQT_GAS_USED } from './market_operation_utils/constants';
 | 
			
		||||
import {
 | 
			
		||||
    CollapsedFill,
 | 
			
		||||
    DexSample,
 | 
			
		||||
@@ -22,6 +23,7 @@ export interface QuoteReportEntryBase {
 | 
			
		||||
    makerAmount: BigNumber;
 | 
			
		||||
    takerAmount: BigNumber;
 | 
			
		||||
    fillData: FillData;
 | 
			
		||||
    gasUsed: BigNumber;
 | 
			
		||||
}
 | 
			
		||||
export interface BridgeQuoteReportEntry extends QuoteReportEntryBase {
 | 
			
		||||
    liquiditySource: Exclude<ERC20BridgeSource, ERC20BridgeSource.Native>;
 | 
			
		||||
@@ -140,6 +142,7 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp
 | 
			
		||||
            takerAmount: ds.output,
 | 
			
		||||
            liquiditySource,
 | 
			
		||||
            fillData: ds.fillData,
 | 
			
		||||
            gasUsed: ds.gasUsed,
 | 
			
		||||
        };
 | 
			
		||||
    } else if (marketOperation === MarketOperation.Sell) {
 | 
			
		||||
        return {
 | 
			
		||||
@@ -147,6 +150,7 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp
 | 
			
		||||
            takerAmount: ds.input,
 | 
			
		||||
            liquiditySource,
 | 
			
		||||
            fillData: ds.fillData,
 | 
			
		||||
            gasUsed: ds.gasUsed,
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        throw new Error(`Unexpected marketOperation ${marketOperation}`);
 | 
			
		||||
@@ -171,6 +175,7 @@ export function multiHopSampleToReportSource(
 | 
			
		||||
            takerAmount: ds.output,
 | 
			
		||||
            fillData: ds.fillData,
 | 
			
		||||
            hopSources: [firstHop.source, secondHop.source],
 | 
			
		||||
            gasUsed: ds.gasUsed,
 | 
			
		||||
        };
 | 
			
		||||
    } else if (marketOperation === MarketOperation.Sell) {
 | 
			
		||||
        return {
 | 
			
		||||
@@ -179,6 +184,7 @@ export function multiHopSampleToReportSource(
 | 
			
		||||
            takerAmount: ds.input,
 | 
			
		||||
            fillData: ds.fillData,
 | 
			
		||||
            hopSources: [firstHop.source, secondHop.source],
 | 
			
		||||
            gasUsed: ds.gasUsed,
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        throw new Error(`Unexpected marketOperation ${marketOperation}`);
 | 
			
		||||
@@ -223,6 +229,7 @@ export function nativeOrderToReportEntry(
 | 
			
		||||
            ...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}),
 | 
			
		||||
            nativeOrder,
 | 
			
		||||
            fillData,
 | 
			
		||||
            gasUsed: NATIVE_RFQT_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        // tslint:disable-next-line: no-object-literal-type-assertion
 | 
			
		||||
@@ -231,6 +238,7 @@ export function nativeOrderToReportEntry(
 | 
			
		||||
            ...nativeOrderBase,
 | 
			
		||||
            isRfqt: false,
 | 
			
		||||
            fillData,
 | 
			
		||||
            gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import { BigNumber } from '@0x/utils';
 | 
			
		||||
import { constants } from '../constants';
 | 
			
		||||
import { MarketOperation } from '../types';
 | 
			
		||||
 | 
			
		||||
import { FeeSchedule, NativeLimitOrderFillData, OptimizedMarketOrder } from './market_operation_utils/types';
 | 
			
		||||
import { NativeLimitOrderFillData, OptimizedMarketOrder } from './market_operation_utils/types';
 | 
			
		||||
import { getNativeAdjustedTakerFeeAmount } from './utils';
 | 
			
		||||
 | 
			
		||||
const { PROTOCOL_FEE_MULTIPLIER, ZERO_AMOUNT } = constants;
 | 
			
		||||
@@ -72,13 +72,11 @@ export interface QuoteFillInfo {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface QuoteFillInfoOpts {
 | 
			
		||||
    gasSchedule: FeeSchedule;
 | 
			
		||||
    protocolFeeMultiplier: BigNumber;
 | 
			
		||||
    slippage: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEFAULT_SIMULATED_FILL_QUOTE_INFO_OPTS: QuoteFillInfoOpts = {
 | 
			
		||||
    gasSchedule: {},
 | 
			
		||||
    protocolFeeMultiplier: PROTOCOL_FEE_MULTIPLIER,
 | 
			
		||||
    slippage: 0,
 | 
			
		||||
};
 | 
			
		||||
@@ -108,7 +106,6 @@ export function simulateBestCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
 | 
			
		||||
        createBestCaseFillOrderCalls(quoteInfo),
 | 
			
		||||
        quoteInfo.fillAmount,
 | 
			
		||||
        protocolFeePerFillOrder,
 | 
			
		||||
        opts.gasSchedule,
 | 
			
		||||
    );
 | 
			
		||||
    return fromIntermediateQuoteFillResult(result, quoteInfo);
 | 
			
		||||
}
 | 
			
		||||
@@ -122,9 +119,9 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
 | 
			
		||||
    const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier);
 | 
			
		||||
    const bestCase = createBestCaseFillOrderCalls(quoteInfo);
 | 
			
		||||
    const result = {
 | 
			
		||||
        ...fillQuoteOrders(bestCase, quoteInfo.fillAmount, protocolFeePerFillOrder, opts.gasSchedule),
 | 
			
		||||
        ...fillQuoteOrders(bestCase, quoteInfo.fillAmount, protocolFeePerFillOrder),
 | 
			
		||||
        // Worst case gas and protocol fee is hitting all orders.
 | 
			
		||||
        gas: getTotalGasUsedByFills(quoteInfo.orders, opts.gasSchedule),
 | 
			
		||||
        gas: getTotalGasUsedByFills(quoteInfo.orders),
 | 
			
		||||
        protocolFee: protocolFeePerFillOrder.times(quoteInfo.orders.filter(o => hasProtocolFee(o)).length),
 | 
			
		||||
    };
 | 
			
		||||
    // Adjust the output by 1-slippage for the worst case if it is a sell
 | 
			
		||||
@@ -140,7 +137,6 @@ export function fillQuoteOrders(
 | 
			
		||||
    fillOrders: QuoteFillOrderCall[],
 | 
			
		||||
    inputAmount: BigNumber,
 | 
			
		||||
    protocolFeePerFillOrder: BigNumber,
 | 
			
		||||
    gasSchedule: FeeSchedule,
 | 
			
		||||
): IntermediateQuoteFillResult {
 | 
			
		||||
    const result: IntermediateQuoteFillResult = {
 | 
			
		||||
        ...EMPTY_QUOTE_INTERMEDIATE_FILL_RESULT,
 | 
			
		||||
@@ -155,9 +151,8 @@ export function fillQuoteOrders(
 | 
			
		||||
            if (remainingInput.lte(0)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            const { source, fillData } = fill;
 | 
			
		||||
            const gas = gasSchedule[source] === undefined ? 0 : gasSchedule[source]!(fillData);
 | 
			
		||||
            result.gas += new BigNumber(gas).toNumber();
 | 
			
		||||
            const { source, gasUsed } = fill;
 | 
			
		||||
            result.gas += new BigNumber(gasUsed).toNumber();
 | 
			
		||||
            result.inputBySource[source] = result.inputBySource[source] || ZERO_AMOUNT;
 | 
			
		||||
 | 
			
		||||
            // Actual rates are rarely linear, so fill subfills individually to
 | 
			
		||||
@@ -314,11 +309,10 @@ function fromIntermediateQuoteFillResult(ir: IntermediateQuoteFillResult, quoteI
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getTotalGasUsedByFills(fills: OptimizedMarketOrder[], gasSchedule: FeeSchedule): number {
 | 
			
		||||
function getTotalGasUsedByFills(fills: OptimizedMarketOrder[]): number {
 | 
			
		||||
    let gasUsed = 0;
 | 
			
		||||
    for (const f of fills) {
 | 
			
		||||
        const fee = gasSchedule[f.source] === undefined ? 0 : gasSchedule[f.source]!(f.fillData);
 | 
			
		||||
        gasUsed += new BigNumber(fee).toNumber();
 | 
			
		||||
        gasUsed += new BigNumber(f.gasUsed).toNumber();
 | 
			
		||||
    }
 | 
			
		||||
    return gasUsed;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,5 +4,8 @@
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
export * from '../generated-wrappers/balance_checker';
 | 
			
		||||
export * from '../generated-wrappers/delegate_hacked_erc20';
 | 
			
		||||
export * from '../generated-wrappers/erc20_bridge_sampler';
 | 
			
		||||
export * from '../generated-wrappers/fake_taker';
 | 
			
		||||
export * from '../generated-wrappers/gas_overhead';
 | 
			
		||||
export * from '../generated-wrappers/hacked_erc20';
 | 
			
		||||
 
 | 
			
		||||
@@ -5,28 +5,23 @@
 | 
			
		||||
 */
 | 
			
		||||
import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
 | 
			
		||||
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
 | 
			
		||||
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
 | 
			
		||||
import * as BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json';
 | 
			
		||||
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
 | 
			
		||||
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
 | 
			
		||||
import * as CurveV2Sampler from '../test/generated-artifacts/CurveV2Sampler.json';
 | 
			
		||||
import * as DelegateHackedERC20 from '../test/generated-artifacts/DelegateHackedERC20.json';
 | 
			
		||||
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
 | 
			
		||||
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
 | 
			
		||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
 | 
			
		||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
 | 
			
		||||
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
 | 
			
		||||
import * as FakeTaker from '../test/generated-artifacts/FakeTaker.json';
 | 
			
		||||
import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
 | 
			
		||||
import * as IBancor from '../test/generated-artifacts/IBancor.json';
 | 
			
		||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
 | 
			
		||||
import * as GasOverhead from '../test/generated-artifacts/GasOverhead.json';
 | 
			
		||||
import * as HackedERC20 from '../test/generated-artifacts/HackedERC20.json';
 | 
			
		||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
 | 
			
		||||
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
 | 
			
		||||
import * as IMooniswap from '../test/generated-artifacts/IMooniswap.json';
 | 
			
		||||
import * as IMStable from '../test/generated-artifacts/IMStable.json';
 | 
			
		||||
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
 | 
			
		||||
import * as IShell from '../test/generated-artifacts/IShell.json';
 | 
			
		||||
import * as ISmoothy from '../test/generated-artifacts/ISmoothy.json';
 | 
			
		||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
 | 
			
		||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
 | 
			
		||||
import * as KyberDmmSampler from '../test/generated-artifacts/KyberDmmSampler.json';
 | 
			
		||||
@@ -36,11 +31,9 @@ import * as LiquidityProviderSampler from '../test/generated-artifacts/Liquidity
 | 
			
		||||
import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json';
 | 
			
		||||
import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json';
 | 
			
		||||
import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json';
 | 
			
		||||
import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json';
 | 
			
		||||
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
 | 
			
		||||
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
 | 
			
		||||
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
 | 
			
		||||
import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json';
 | 
			
		||||
import * as SwapRevertSampler from '../test/generated-artifacts/SwapRevertSampler.json';
 | 
			
		||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
 | 
			
		||||
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
 | 
			
		||||
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
 | 
			
		||||
@@ -49,17 +42,20 @@ import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.
 | 
			
		||||
import * as UniswapV3Sampler from '../test/generated-artifacts/UniswapV3Sampler.json';
 | 
			
		||||
import * as UtilitySampler from '../test/generated-artifacts/UtilitySampler.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    ApproximateBuys: ApproximateBuys as ContractArtifact,
 | 
			
		||||
    BalanceChecker: BalanceChecker as ContractArtifact,
 | 
			
		||||
    BalancerSampler: BalancerSampler as ContractArtifact,
 | 
			
		||||
    BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
 | 
			
		||||
    BancorSampler: BancorSampler as ContractArtifact,
 | 
			
		||||
    CurveSampler: CurveSampler as ContractArtifact,
 | 
			
		||||
    CurveV2Sampler: CurveV2Sampler as ContractArtifact,
 | 
			
		||||
    DODOSampler: DODOSampler as ContractArtifact,
 | 
			
		||||
    DODOV2Sampler: DODOV2Sampler as ContractArtifact,
 | 
			
		||||
    DelegateHackedERC20: DelegateHackedERC20 as ContractArtifact,
 | 
			
		||||
    ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    Eth2DaiSampler: Eth2DaiSampler as ContractArtifact,
 | 
			
		||||
    FakeTaker: FakeTaker as ContractArtifact,
 | 
			
		||||
    GasOverhead: GasOverhead as ContractArtifact,
 | 
			
		||||
    HackedERC20: HackedERC20 as ContractArtifact,
 | 
			
		||||
    KyberDmmSampler: KyberDmmSampler as ContractArtifact,
 | 
			
		||||
    KyberSampler: KyberSampler as ContractArtifact,
 | 
			
		||||
    LidoSampler: LidoSampler as ContractArtifact,
 | 
			
		||||
@@ -67,26 +63,16 @@ export const artifacts = {
 | 
			
		||||
    MStableSampler: MStableSampler as ContractArtifact,
 | 
			
		||||
    MakerPSMSampler: MakerPSMSampler as ContractArtifact,
 | 
			
		||||
    MooniswapSampler: MooniswapSampler as ContractArtifact,
 | 
			
		||||
    MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
 | 
			
		||||
    NativeOrderSampler: NativeOrderSampler as ContractArtifact,
 | 
			
		||||
    SamplerUtils: SamplerUtils as ContractArtifact,
 | 
			
		||||
    ShellSampler: ShellSampler as ContractArtifact,
 | 
			
		||||
    SmoothySampler: SmoothySampler as ContractArtifact,
 | 
			
		||||
    SwapRevertSampler: SwapRevertSampler as ContractArtifact,
 | 
			
		||||
    TwoHopSampler: TwoHopSampler as ContractArtifact,
 | 
			
		||||
    UniswapSampler: UniswapSampler as ContractArtifact,
 | 
			
		||||
    UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
 | 
			
		||||
    UniswapV3Sampler: UniswapV3Sampler as ContractArtifact,
 | 
			
		||||
    UtilitySampler: UtilitySampler as ContractArtifact,
 | 
			
		||||
    IBalancer: IBalancer as ContractArtifact,
 | 
			
		||||
    IBancor: IBancor as ContractArtifact,
 | 
			
		||||
    ICurve: ICurve as ContractArtifact,
 | 
			
		||||
    IEth2Dai: IEth2Dai as ContractArtifact,
 | 
			
		||||
    IKyberNetwork: IKyberNetwork as ContractArtifact,
 | 
			
		||||
    IMStable: IMStable as ContractArtifact,
 | 
			
		||||
    IMooniswap: IMooniswap as ContractArtifact,
 | 
			
		||||
    IMultiBridge: IMultiBridge as ContractArtifact,
 | 
			
		||||
    IShell: IShell as ContractArtifact,
 | 
			
		||||
    ISmoothy: ISmoothy as ContractArtifact,
 | 
			
		||||
    IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
 | 
			
		||||
    IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
 | 
			
		||||
    DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ const expect = chai.expect;
 | 
			
		||||
const DAI_TOKEN = '0x6b175474e89094c44da98b954eedeac495271d0f';
 | 
			
		||||
const ETH_TOKEN = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
 | 
			
		||||
const GAS_PRICE = new BigNumber(50e9); // 50 gwei
 | 
			
		||||
const NATIVE_ORDER_FEE = new BigNumber(220e3); // 220K gas
 | 
			
		||||
 | 
			
		||||
// DEX samples to fill in MarketSideLiquidity
 | 
			
		||||
const kyberSample1: DexSample = {
 | 
			
		||||
@@ -26,20 +25,19 @@ const kyberSample1: DexSample = {
 | 
			
		||||
    input: new BigNumber(10000),
 | 
			
		||||
    output: new BigNumber(10001),
 | 
			
		||||
    fillData: {},
 | 
			
		||||
    gasUsed: new BigNumber(450000),
 | 
			
		||||
};
 | 
			
		||||
const uniswapSample1: DexSample = {
 | 
			
		||||
    source: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
    input: new BigNumber(10003),
 | 
			
		||||
    output: new BigNumber(10004),
 | 
			
		||||
    fillData: {},
 | 
			
		||||
    gasUsed: new BigNumber(90000),
 | 
			
		||||
};
 | 
			
		||||
const dexQuotes: DexSample[] = [kyberSample1, uniswapSample1];
 | 
			
		||||
 | 
			
		||||
const feeSchedule = {
 | 
			
		||||
    [ERC20BridgeSource.Native]: _.constant(GAS_PRICE.times(NATIVE_ORDER_FEE)),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const exchangeProxyOverhead = (sourceFlags: number) => {
 | 
			
		||||
// At this stage it has been gas price adjusted
 | 
			
		||||
const exchangeProxyOverhead = (sourceFlags: bigint) => {
 | 
			
		||||
    if ([SOURCE_FLAGS.RfqOrder].includes(sourceFlags)) {
 | 
			
		||||
        return new BigNumber(20e3).times(GAS_PRICE);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -102,14 +100,14 @@ describe('getComparisonPrices', async () => {
 | 
			
		||||
            adjustedRate,
 | 
			
		||||
            AMOUNT,
 | 
			
		||||
            sellMarketSideLiquidity,
 | 
			
		||||
            feeSchedule,
 | 
			
		||||
            GAS_PRICE,
 | 
			
		||||
            exchangeProxyOverhead,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // expected outcome
 | 
			
		||||
        const EXPECTED_PRICE = new BigNumber('500.6');
 | 
			
		||||
        const EXPECTED_PRICE = new BigNumber('500.30');
 | 
			
		||||
 | 
			
		||||
        expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE);
 | 
			
		||||
        expect(comparisonPrices.wholeOrder).to.be.bignumber.eq(EXPECTED_PRICE);
 | 
			
		||||
    });
 | 
			
		||||
    it('should create a proper comparison price for Buys', () => {
 | 
			
		||||
        // test buying 10 ETH with DAI
 | 
			
		||||
@@ -124,14 +122,14 @@ describe('getComparisonPrices', async () => {
 | 
			
		||||
            adjustedRate,
 | 
			
		||||
            AMOUNT,
 | 
			
		||||
            buyMarketSideLiquidity,
 | 
			
		||||
            feeSchedule,
 | 
			
		||||
            GAS_PRICE,
 | 
			
		||||
            exchangeProxyOverhead,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // expected outcome
 | 
			
		||||
        const EXPECTED_PRICE = new BigNumber('0.0020024029');
 | 
			
		||||
        const EXPECTED_PRICE = new BigNumber('0.0020012007');
 | 
			
		||||
 | 
			
		||||
        expect(comparisonPrices.wholeOrder).to.deep.eq(EXPECTED_PRICE);
 | 
			
		||||
        expect(comparisonPrices.wholeOrder).to.be.bignumber.eq(EXPECTED_PRICE);
 | 
			
		||||
    });
 | 
			
		||||
    it('should not return a price if takerAmount is < 0', () => {
 | 
			
		||||
        // test selling 0.00001 ETH for DAI
 | 
			
		||||
@@ -144,7 +142,7 @@ describe('getComparisonPrices', async () => {
 | 
			
		||||
            adjustedRate,
 | 
			
		||||
            AMOUNT,
 | 
			
		||||
            sellMarketSideLiquidity,
 | 
			
		||||
            feeSchedule,
 | 
			
		||||
            GAS_PRICE,
 | 
			
		||||
            exchangeProxyOverhead,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@ import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine
 | 
			
		||||
import { RPCSubprovider } from '@0x/subproviders';
 | 
			
		||||
import { BigNumber, NULL_BYTES, providerUtils } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
import { ERC20BridgeSource } from '../../src';
 | 
			
		||||
import { getCurveLikeInfosForPair } from '../../src/utils/market_operation_utils/bridge_source_utils';
 | 
			
		||||
import { KYBER_CONFIG_BY_CHAIN_ID, MAINNET_TOKENS } from '../../src/utils/market_operation_utils/constants';
 | 
			
		||||
import { artifacts } from '../artifacts';
 | 
			
		||||
import { ERC20BridgeSamplerContract } from '../wrappers';
 | 
			
		||||
@@ -30,27 +32,50 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    describe('Curve', () => {
 | 
			
		||||
        const CURVE_ADDRESS = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
 | 
			
		||||
        const DAI_TOKEN_INDEX = new BigNumber(0);
 | 
			
		||||
        const USDC_TOKEN_INDEX = new BigNumber(1);
 | 
			
		||||
        const CURVE_INFO = {
 | 
			
		||||
            poolAddress: CURVE_ADDRESS,
 | 
			
		||||
            sellQuoteFunctionSelector: '0x07211ef7',
 | 
			
		||||
            buyQuoteFunctionSelector: '0x0e71d1b9',
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        describe('sampleSellsFromCurve()', () => {
 | 
			
		||||
            it('samples sells from Curve DAI->USDC', async () => {
 | 
			
		||||
                const CURVE_INFO = getCurveLikeInfosForPair(
 | 
			
		||||
                    ChainId.Mainnet,
 | 
			
		||||
                    MAINNET_TOKENS.DAI,
 | 
			
		||||
                    MAINNET_TOKENS.USDC,
 | 
			
		||||
                    ERC20BridgeSource.Curve,
 | 
			
		||||
                )[0];
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .sampleSellsFromCurve(
 | 
			
		||||
                        {
 | 
			
		||||
                            curveAddress: CURVE_INFO.poolAddress,
 | 
			
		||||
                            fromCoinIdx: new BigNumber(CURVE_INFO.takerTokenIdx),
 | 
			
		||||
                            toCoinIdx: new BigNumber(CURVE_INFO.makerTokenIdx),
 | 
			
		||||
                            exchangeFunctionSelector: CURVE_INFO.exchangeFunctionSelector,
 | 
			
		||||
                        },
 | 
			
		||||
                        MAINNET_TOKENS.DAI,
 | 
			
		||||
                        MAINNET_TOKENS.USDC,
 | 
			
		||||
                        [toBaseUnitAmount(1)],
 | 
			
		||||
                    )
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('samples sells from Curve USDC->DAI', async () => {
 | 
			
		||||
                const CURVE_INFO = getCurveLikeInfosForPair(
 | 
			
		||||
                    ChainId.Mainnet,
 | 
			
		||||
                    MAINNET_TOKENS.USDC,
 | 
			
		||||
                    MAINNET_TOKENS.DAI,
 | 
			
		||||
                    ERC20BridgeSource.Curve,
 | 
			
		||||
                )[0];
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .sampleSellsFromCurve(
 | 
			
		||||
                        {
 | 
			
		||||
                            curveAddress: CURVE_INFO.poolAddress,
 | 
			
		||||
                            fromCoinIdx: new BigNumber(CURVE_INFO.takerTokenIdx),
 | 
			
		||||
                            toCoinIdx: new BigNumber(CURVE_INFO.makerTokenIdx),
 | 
			
		||||
                            exchangeFunctionSelector: CURVE_INFO.exchangeFunctionSelector,
 | 
			
		||||
                        },
 | 
			
		||||
                        MAINNET_TOKENS.USDC,
 | 
			
		||||
                        MAINNET_TOKENS.DAI,
 | 
			
		||||
                        [toBaseUnitAmount(1, 6)],
 | 
			
		||||
                    )
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -58,11 +83,27 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe('sampleBuysFromCurve()', () => {
 | 
			
		||||
            const CURVE_INFO = getCurveLikeInfosForPair(
 | 
			
		||||
                ChainId.Mainnet,
 | 
			
		||||
                MAINNET_TOKENS.DAI,
 | 
			
		||||
                MAINNET_TOKENS.USDC,
 | 
			
		||||
                ERC20BridgeSource.Curve,
 | 
			
		||||
            )[0];
 | 
			
		||||
            it('samples buys from Curve DAI->USDC', async () => {
 | 
			
		||||
                // From DAI to USDC
 | 
			
		||||
                // I want to buy 1 USDC
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .sampleBuysFromCurve(
 | 
			
		||||
                        {
 | 
			
		||||
                            curveAddress: CURVE_INFO.poolAddress,
 | 
			
		||||
                            fromCoinIdx: new BigNumber(CURVE_INFO.takerTokenIdx),
 | 
			
		||||
                            toCoinIdx: new BigNumber(CURVE_INFO.makerTokenIdx),
 | 
			
		||||
                            exchangeFunctionSelector: CURVE_INFO.exchangeFunctionSelector,
 | 
			
		||||
                        },
 | 
			
		||||
                        MAINNET_TOKENS.DAI,
 | 
			
		||||
                        MAINNET_TOKENS.USDC,
 | 
			
		||||
                        [toBaseUnitAmount(1, 6)],
 | 
			
		||||
                    )
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -72,7 +113,17 @@ blockchainTests.skip('Mainnet Sampler Tests', env => {
 | 
			
		||||
                // From USDC to DAI
 | 
			
		||||
                // I want to buy 1 DAI
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .sampleBuysFromCurve(
 | 
			
		||||
                        {
 | 
			
		||||
                            curveAddress: CURVE_INFO.poolAddress,
 | 
			
		||||
                            fromCoinIdx: new BigNumber(CURVE_INFO.takerTokenIdx),
 | 
			
		||||
                            toCoinIdx: new BigNumber(CURVE_INFO.makerTokenIdx),
 | 
			
		||||
                            exchangeFunctionSelector: CURVE_INFO.exchangeFunctionSelector,
 | 
			
		||||
                        },
 | 
			
		||||
                        MAINNET_TOKENS.USDC,
 | 
			
		||||
                        MAINNET_TOKENS.DAI,
 | 
			
		||||
                        [toBaseUnitAmount(1)],
 | 
			
		||||
                    )
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,8 @@ import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '
 | 
			
		||||
// tslint:disable: custom-no-magic-numbers
 | 
			
		||||
 | 
			
		||||
const { NULL_ADDRESS } = constants;
 | 
			
		||||
blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
// jacob: Skip until we can override in Ganache
 | 
			
		||||
blockchainTests.skip('erc20-bridge-sampler', env => {
 | 
			
		||||
    let testContract: TestERC20BridgeSamplerContract;
 | 
			
		||||
    const RATE_DENOMINATOR = constants.ONE_ETHER;
 | 
			
		||||
    const MIN_RATE = new BigNumber('0.01');
 | 
			
		||||
@@ -37,7 +38,6 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
    const KYBER_RESERVE_OFFSET = new BigNumber(0);
 | 
			
		||||
    let KYBER_ADDRESS = '';
 | 
			
		||||
    let ETH2DAI_ADDRESS = '';
 | 
			
		||||
    let UNISWAP_ADDRESS = '';
 | 
			
		||||
    let UNISWAP_V2_ROUTER = '';
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
@@ -50,7 +50,6 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
        UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync();
 | 
			
		||||
        KYBER_ADDRESS = await testContract.kyber().callAsync();
 | 
			
		||||
        ETH2DAI_ADDRESS = await testContract.eth2Dai().callAsync();
 | 
			
		||||
        UNISWAP_ADDRESS = await testContract.uniswap().callAsync();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    function getPackedHash(...args: string[]): string {
 | 
			
		||||
@@ -660,198 +659,6 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    blockchainTests.resets('sampleSellsFromUniswap()', () => {
 | 
			
		||||
        const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
 | 
			
		||||
        before(async () => {
 | 
			
		||||
            await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('throws if tokens are the same', async () => {
 | 
			
		||||
            const tx = testContract.sampleSellsFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
 | 
			
		||||
            return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can return no quotes', async () => {
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq([]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote token -> token', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if token -> token fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote token -> ETH', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if token -> ETH fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote ETH -> token', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicSellQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if ETH -> token fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if no exchange exists for the maker token', async () => {
 | 
			
		||||
            const nonExistantToken = randomAddress();
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if no exchange exists for the taker token', async () => {
 | 
			
		||||
            const nonExistantToken = randomAddress();
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(nonExistantToken);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleSellsFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    blockchainTests.resets('sampleBuysFromUniswap()', () => {
 | 
			
		||||
        const UNISWAP_ETH_ADDRESS = NULL_ADDRESS;
 | 
			
		||||
        before(async () => {
 | 
			
		||||
            await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('throws if tokens are the same', async () => {
 | 
			
		||||
            const tx = testContract.sampleBuysFromUniswap(UNISWAP_ADDRESS, MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
 | 
			
		||||
            return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can return no quotes', async () => {
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, [])
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq([]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote token -> token', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if token -> token fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote token -> ETH', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if token -> ETH fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, UNISWAP_ETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can quote ETH -> token', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Uniswap'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if ETH -> token fails', async () => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, UNISWAP_ETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if no exchange exists for the maker token', async () => {
 | 
			
		||||
            const nonExistantToken = randomAddress();
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(nonExistantToken);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, TAKER_TOKEN, nonExistantToken, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('returns zero if no exchange exists for the taker token', async () => {
 | 
			
		||||
            const nonExistantToken = randomAddress();
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromUniswap(UNISWAP_ADDRESS, nonExistantToken, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('liquidity provider', () => {
 | 
			
		||||
        const xAsset = randomAddress();
 | 
			
		||||
        const yAsset = randomAddress();
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return ['0x', '0x', expectedMakerFillAmounts];
 | 
			
		||||
                    return ['0x', '0x', [], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -164,7 +164,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [results] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getKyberSellQuotes(
 | 
			
		||||
                    { hintHandler: randomAddress(), networkProxy: randomAddress(), weth: randomAddress() },
 | 
			
		||||
                    new BigNumber(0),
 | 
			
		||||
@@ -173,7 +173,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expectedTakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
            expect(results.samples).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('getLiquidityProviderSellQuotes()', async () => {
 | 
			
		||||
@@ -186,7 +186,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(providerAddress).to.eq(poolAddress);
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    return [toBaseUnitAmount(1001)];
 | 
			
		||||
                    return [[new BigNumber(gasCost)], [toBaseUnitAmount(1001)]];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -215,6 +215,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                        output: toBaseUnitAmount(1001),
 | 
			
		||||
                        input: toBaseUnitAmount(1000),
 | 
			
		||||
                        fillData: { poolAddress, gasCost },
 | 
			
		||||
                        gasUsed: new BigNumber(gasCost),
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
            ]);
 | 
			
		||||
@@ -230,7 +231,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(providerAddress).to.eq(poolAddress);
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    return [toBaseUnitAmount(999)];
 | 
			
		||||
                    return [[new BigNumber(gasCost)], [toBaseUnitAmount(999)]];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -259,6 +260,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                        output: toBaseUnitAmount(999),
 | 
			
		||||
                        input: toBaseUnitAmount(1000),
 | 
			
		||||
                        fillData: { poolAddress, gasCost },
 | 
			
		||||
                        gasUsed: new BigNumber(gasCost),
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
            ]);
 | 
			
		||||
@@ -274,7 +276,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return expectedMakerFillAmounts;
 | 
			
		||||
                    return [[], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -286,7 +288,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [result] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getEth2DaiSellQuotes(
 | 
			
		||||
                    randomAddress(),
 | 
			
		||||
                    expectedMakerToken,
 | 
			
		||||
@@ -294,7 +296,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expectedTakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
            expect(result.samples).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('getUniswapSellQuotes()', async () => {
 | 
			
		||||
@@ -307,7 +309,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return expectedMakerFillAmounts;
 | 
			
		||||
                    return [[], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -319,7 +321,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [results] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getUniswapSellQuotes(
 | 
			
		||||
                    randomAddress(),
 | 
			
		||||
                    expectedMakerToken,
 | 
			
		||||
@@ -327,7 +329,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expectedTakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
            expect(results.samples).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('getUniswapV2SellQuotes()', async () => {
 | 
			
		||||
@@ -339,7 +341,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                sampleSellsFromUniswapV2: (_router, path, fillAmounts) => {
 | 
			
		||||
                    expect(path).to.deep.eq([expectedMakerToken, expectedTakerToken]);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return expectedMakerFillAmounts;
 | 
			
		||||
                    return [[], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -351,14 +353,14 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [results] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getUniswapV2SellQuotes(
 | 
			
		||||
                    NULL_ADDRESS,
 | 
			
		||||
                    [expectedMakerToken, expectedTakerToken],
 | 
			
		||||
                    expectedTakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
            expect(results.samples).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('getEth2DaiBuyQuotes()', async () => {
 | 
			
		||||
@@ -371,7 +373,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
                    return expectedTakerFillAmounts;
 | 
			
		||||
                    return [[], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -383,7 +385,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [results] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getEth2DaiBuyQuotes(
 | 
			
		||||
                    randomAddress(),
 | 
			
		||||
                    expectedMakerToken,
 | 
			
		||||
@@ -391,7 +393,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expectedMakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
            expect(results.samples).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('getUniswapBuyQuotes()', async () => {
 | 
			
		||||
@@ -404,7 +406,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
                    return expectedTakerFillAmounts;
 | 
			
		||||
                    return [[], expectedMakerFillAmounts];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -416,7 +418,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                undefined,
 | 
			
		||||
                async () => undefined,
 | 
			
		||||
            );
 | 
			
		||||
            const [fillableAmounts] = await dexOrderSampler.executeAsync(
 | 
			
		||||
            const [results] = await dexOrderSampler.executeAsync(
 | 
			
		||||
                dexOrderSampler.getUniswapBuyQuotes(
 | 
			
		||||
                    randomAddress(),
 | 
			
		||||
                    expectedMakerToken,
 | 
			
		||||
@@ -424,7 +426,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expectedMakerFillAmounts,
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
            expect(fillableAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
            expect(results.samples).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        interface RatesBySource {
 | 
			
		||||
@@ -445,20 +447,27 @@ describe('DexSampler tests', () => {
 | 
			
		||||
            let uniswapRouter: string;
 | 
			
		||||
            let uniswapV2Router: string;
 | 
			
		||||
            let eth2DaiRouter: string;
 | 
			
		||||
            const gasUsed = new BigNumber(123);
 | 
			
		||||
            const sampler = new MockSamplerContract({
 | 
			
		||||
                sampleSellsFromUniswap: (router, takerToken, makerToken, fillAmounts) => {
 | 
			
		||||
                    uniswapRouter = router;
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
                sampleSellsFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => {
 | 
			
		||||
                    eth2DaiRouter = router;
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
                sampleSellsFromUniswapV2: (router, path, fillAmounts) => {
 | 
			
		||||
                    uniswapV2Router = router;
 | 
			
		||||
@@ -470,7 +479,10 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                        expect(path).to.have.lengthOf.within(2, 3);
 | 
			
		||||
                    }
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedTakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -495,6 +507,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    source: s,
 | 
			
		||||
                    input: a,
 | 
			
		||||
                    output: a.times(ratesBySource[s]).integerValue(),
 | 
			
		||||
                    gasUsed,
 | 
			
		||||
                    fillData: (() => {
 | 
			
		||||
                        if (s === ERC20BridgeSource.UniswapV2) {
 | 
			
		||||
                            return {
 | 
			
		||||
@@ -518,6 +531,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    source: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
                    input: a,
 | 
			
		||||
                    output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(),
 | 
			
		||||
                    gasUsed,
 | 
			
		||||
                    fillData: {
 | 
			
		||||
                        router: uniswapV2Router,
 | 
			
		||||
                        tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken],
 | 
			
		||||
@@ -539,6 +553,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                [ERC20BridgeSource.UniswapV2]: getRandomFloat(0, 100),
 | 
			
		||||
            };
 | 
			
		||||
            const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
 | 
			
		||||
            const gasUsed = new BigNumber(123);
 | 
			
		||||
            let uniswapRouter: string;
 | 
			
		||||
            let uniswapV2Router: string;
 | 
			
		||||
            let eth2DaiRouter: string;
 | 
			
		||||
@@ -548,14 +563,20 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Uniswap]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
                sampleBuysFromEth2Dai: (router, takerToken, makerToken, fillAmounts) => {
 | 
			
		||||
                    eth2DaiRouter = router;
 | 
			
		||||
                    expect(takerToken).to.eq(expectedTakerToken);
 | 
			
		||||
                    expect(makerToken).to.eq(expectedMakerToken);
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.Eth2Dai]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
                sampleBuysFromUniswapV2: (router, path, fillAmounts) => {
 | 
			
		||||
                    uniswapV2Router = router;
 | 
			
		||||
@@ -567,7 +588,10 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                        expect(path).to.have.lengthOf.within(2, 3);
 | 
			
		||||
                    }
 | 
			
		||||
                    expect(fillAmounts).to.deep.eq(expectedMakerFillAmounts);
 | 
			
		||||
                    return fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue());
 | 
			
		||||
                    return [
 | 
			
		||||
                        fillAmounts.map(_a => gasUsed),
 | 
			
		||||
                        fillAmounts.map(a => a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue()),
 | 
			
		||||
                    ];
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            const dexOrderSampler = new DexOrderSampler(
 | 
			
		||||
@@ -587,6 +611,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    source: s,
 | 
			
		||||
                    input: a,
 | 
			
		||||
                    output: a.times(ratesBySource[s]).integerValue(),
 | 
			
		||||
                    gasUsed,
 | 
			
		||||
                    fillData: (() => {
 | 
			
		||||
                        if (s === ERC20BridgeSource.UniswapV2) {
 | 
			
		||||
                            return {
 | 
			
		||||
@@ -609,6 +634,7 @@ describe('DexSampler tests', () => {
 | 
			
		||||
                    source: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
                    input: a,
 | 
			
		||||
                    output: a.times(ratesBySource[ERC20BridgeSource.UniswapV2]).integerValue(),
 | 
			
		||||
                    gasUsed,
 | 
			
		||||
                    fillData: {
 | 
			
		||||
                        router: uniswapV2Router,
 | 
			
		||||
                        tokenAddressPath: [expectedTakerToken, wethAddress, expectedMakerToken],
 | 
			
		||||
 
 | 
			
		||||
@@ -102,6 +102,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
 | 
			
		||||
            takerAmount: order.takerAmount,
 | 
			
		||||
            fills: [],
 | 
			
		||||
            ...optimizerFields,
 | 
			
		||||
            gasUsed: new BigNumber(1),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import { NativeOrderWithFillableAmounts } from '../src/types';
 | 
			
		||||
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
 | 
			
		||||
import {
 | 
			
		||||
    BUY_SOURCE_FILTER_BY_CHAIN_ID,
 | 
			
		||||
    DEFAULT_GET_MARKET_ORDERS_OPTS,
 | 
			
		||||
    POSITIVE_INF,
 | 
			
		||||
    SELL_SOURCE_FILTER_BY_CHAIN_ID,
 | 
			
		||||
    SOURCE_FLAGS,
 | 
			
		||||
@@ -62,6 +63,7 @@ const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
 | 
			
		||||
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
 | 
			
		||||
 | 
			
		||||
const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign };
 | 
			
		||||
const GAS_PRICE = new BigNumber(1);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * gets the orders required for a market sell operation by (potentially) merging native orders with
 | 
			
		||||
@@ -75,7 +77,7 @@ async function getMarketSellOrdersAsync(
 | 
			
		||||
    utils: MarketOperationUtils,
 | 
			
		||||
    nativeOrders: SignedNativeOrder[],
 | 
			
		||||
    takerAmount: BigNumber,
 | 
			
		||||
    opts?: Partial<GetMarketOrdersOpts>,
 | 
			
		||||
    opts: GetMarketOrdersOpts,
 | 
			
		||||
): Promise<OptimizerResultWithReport> {
 | 
			
		||||
    return utils.getOptimizerResultAsync(nativeOrders, takerAmount, MarketOperation.Sell, opts);
 | 
			
		||||
}
 | 
			
		||||
@@ -92,7 +94,7 @@ async function getMarketBuyOrdersAsync(
 | 
			
		||||
    utils: MarketOperationUtils,
 | 
			
		||||
    nativeOrders: SignedNativeOrder[],
 | 
			
		||||
    makerAmount: BigNumber,
 | 
			
		||||
    opts?: Partial<GetMarketOrdersOpts>,
 | 
			
		||||
    opts: GetMarketOrdersOpts,
 | 
			
		||||
): Promise<OptimizerResultWithReport> {
 | 
			
		||||
    return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts);
 | 
			
		||||
}
 | 
			
		||||
@@ -129,6 +131,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
    const contractAddresses = {
 | 
			
		||||
        ...getContractAddressesForChainOrThrow(CHAIN_ID),
 | 
			
		||||
    };
 | 
			
		||||
    let DEFAULT_DEX_GAS_USED = new BigNumber(100000);
 | 
			
		||||
 | 
			
		||||
    function getMockedQuoteRequestor(
 | 
			
		||||
        type: 'indicative' | 'firm',
 | 
			
		||||
@@ -205,6 +208,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
        inputs: Numberish[],
 | 
			
		||||
        rates: Numberish[],
 | 
			
		||||
        fillData?: FillData,
 | 
			
		||||
        gasUsed?: BigNumber,
 | 
			
		||||
    ): DexSample[] {
 | 
			
		||||
        const samples: DexSample[] = [];
 | 
			
		||||
        inputs.forEach((input, i) => {
 | 
			
		||||
@@ -218,6 +222,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    .times(rate)
 | 
			
		||||
                    .plus(i === 0 ? 0 : samples[i - 1].output)
 | 
			
		||||
                    .integerValue(),
 | 
			
		||||
                gasUsed: gasUsed || DEFAULT_DEX_GAS_USED,
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        return samples;
 | 
			
		||||
@@ -233,7 +238,10 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
        liquidityProviderAddress?: string,
 | 
			
		||||
    ) => DexSample[][];
 | 
			
		||||
 | 
			
		||||
    function createGetMultipleSellQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
 | 
			
		||||
    function createGetMultipleSellQuotesOperationFromRates(
 | 
			
		||||
        rates: RatesBySource,
 | 
			
		||||
        gasSchedule: Partial<{ [key in ERC20BridgeSource]: BigNumber }> = {},
 | 
			
		||||
    ): GetMultipleQuotesOperation {
 | 
			
		||||
        return (
 | 
			
		||||
            sources: ERC20BridgeSource[],
 | 
			
		||||
            _makerToken: string,
 | 
			
		||||
@@ -241,11 +249,16 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            fillAmounts: BigNumber[],
 | 
			
		||||
            _wethAddress: string,
 | 
			
		||||
        ) => {
 | 
			
		||||
            return BATCH_SOURCE_FILTERS.getAllowed(sources).map(s => createSamplesFromRates(s, fillAmounts, rates[s]));
 | 
			
		||||
            return BATCH_SOURCE_FILTERS.getAllowed(sources).map(s =>
 | 
			
		||||
                createSamplesFromRates(s, fillAmounts, rates[s], undefined, gasSchedule[s]),
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function createGetMultipleBuyQuotesOperationFromRates(rates: RatesBySource): GetMultipleQuotesOperation {
 | 
			
		||||
    function createGetMultipleBuyQuotesOperationFromRates(
 | 
			
		||||
        rates: RatesBySource,
 | 
			
		||||
        gasSchedule: Partial<{ [key in ERC20BridgeSource]: BigNumber }> = {},
 | 
			
		||||
    ): GetMultipleQuotesOperation {
 | 
			
		||||
        return (
 | 
			
		||||
            sources: ERC20BridgeSource[],
 | 
			
		||||
            _makerToken: string,
 | 
			
		||||
@@ -258,6 +271,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    s,
 | 
			
		||||
                    fillAmounts,
 | 
			
		||||
                    rates[s].map(r => new BigNumber(1).div(r)),
 | 
			
		||||
                    gasSchedule[s],
 | 
			
		||||
                ),
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
@@ -334,8 +348,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                poolAddress: randomAddress(),
 | 
			
		||||
                tokens: [TAKER_TOKEN, MAKER_TOKEN],
 | 
			
		||||
                exchangeFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                sellQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                buyQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
            },
 | 
			
		||||
            fromTokenIdx: 0,
 | 
			
		||||
            toTokenIdx: 1,
 | 
			
		||||
@@ -345,8 +357,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                poolAddress: randomAddress(),
 | 
			
		||||
                tokens: [TAKER_TOKEN, MAKER_TOKEN],
 | 
			
		||||
                exchangeFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                sellQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                buyQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
            },
 | 
			
		||||
            fromTokenIdx: 0,
 | 
			
		||||
            toTokenIdx: 1,
 | 
			
		||||
@@ -356,8 +366,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                poolAddress: randomAddress(),
 | 
			
		||||
                tokens: [TAKER_TOKEN, MAKER_TOKEN],
 | 
			
		||||
                exchangeFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                sellQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                buyQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
            },
 | 
			
		||||
            fromTokenIdx: 0,
 | 
			
		||||
            toTokenIdx: 1,
 | 
			
		||||
@@ -367,8 +375,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                poolAddress: randomAddress(),
 | 
			
		||||
                tokens: [TAKER_TOKEN, MAKER_TOKEN],
 | 
			
		||||
                exchangeFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                sellQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                buyQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
            },
 | 
			
		||||
            fromTokenIdx: 0,
 | 
			
		||||
            toTokenIdx: 1,
 | 
			
		||||
@@ -378,8 +384,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                poolAddress: randomAddress(),
 | 
			
		||||
                tokens: [TAKER_TOKEN, MAKER_TOKEN],
 | 
			
		||||
                exchangeFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                sellQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
                buyQuoteFunctionSelector: hexUtils.random(4),
 | 
			
		||||
            },
 | 
			
		||||
            fromTokenIdx: 0,
 | 
			
		||||
            toTokenIdx: 1,
 | 
			
		||||
@@ -455,15 +459,15 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                FILL_AMOUNT,
 | 
			
		||||
                _.times(NUM_SAMPLES, i => DEFAULT_RATES[ERC20BridgeSource.Native][i]),
 | 
			
		||||
            );
 | 
			
		||||
            const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> = {
 | 
			
		||||
            const DEFAULT_OPTS: GetMarketOrdersOpts = {
 | 
			
		||||
                ...DEFAULT_GET_MARKET_ORDERS_OPTS,
 | 
			
		||||
                numSamples: NUM_SAMPLES,
 | 
			
		||||
                sampleDistributionBase: 1,
 | 
			
		||||
                bridgeSlippage: 0,
 | 
			
		||||
                maxFallbackSlippage: 100,
 | 
			
		||||
                excludedSources: DEFAULT_EXCLUDED,
 | 
			
		||||
                allowFallback: false,
 | 
			
		||||
                gasSchedule: {},
 | 
			
		||||
                feeSchedule: {},
 | 
			
		||||
                gasPrice: GAS_PRICE,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            beforeEach(() => {
 | 
			
		||||
@@ -641,10 +645,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
 | 
			
		||||
                let requestedComparisonPrice: BigNumber | undefined;
 | 
			
		||||
 | 
			
		||||
                // to get a comparisonPrice, you need a feeschedule for a native order
 | 
			
		||||
                const feeSchedule = {
 | 
			
		||||
                    [ERC20BridgeSource.Native]: _.constant(new BigNumber(1)),
 | 
			
		||||
                };
 | 
			
		||||
                mockedQuoteRequestor
 | 
			
		||||
                    .setup(mqr => mqr.getMakerUriForSignature(TypeMoq.It.isValue(SIGNATURE)))
 | 
			
		||||
                    .returns(() => 'https://foo.bar');
 | 
			
		||||
@@ -703,13 +703,13 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                        mou.getMarketSellLiquidityAsync(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()),
 | 
			
		||||
                    )
 | 
			
		||||
                    .returns(async () => {
 | 
			
		||||
                        return {
 | 
			
		||||
                        const marketSellLiquidity: MarketSideLiquidity = {
 | 
			
		||||
                            side: MarketOperation.Sell,
 | 
			
		||||
                            inputAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                            inputToken: MAKER_TOKEN,
 | 
			
		||||
                            outputToken: TAKER_TOKEN,
 | 
			
		||||
                            inputAmountPerEth: Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                            outputAmountPerEth: Web3Wrapper.toBaseUnitAmount(1, 6),
 | 
			
		||||
                            inputAmountPerEth: new BigNumber(1), // selling ETH so 1:1
 | 
			
		||||
                            outputAmountPerEth: new BigNumber(0.00000000241319391), // buying USDC (6 decimal)
 | 
			
		||||
                            quoteSourceFilters: new SourceFilters(),
 | 
			
		||||
                            makerTokenDecimals: 6,
 | 
			
		||||
                            takerTokenDecimals: 18,
 | 
			
		||||
@@ -735,14 +735,15 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                            },
 | 
			
		||||
                            isRfqSupported: true,
 | 
			
		||||
                        };
 | 
			
		||||
 | 
			
		||||
                        return marketSellLiquidity;
 | 
			
		||||
                    });
 | 
			
		||||
                const result = await mockedMarketOpUtils.object.getOptimizerResultAsync(
 | 
			
		||||
                    ORDERS,
 | 
			
		||||
                    Web3Wrapper.toBaseUnitAmount(1, 18),
 | 
			
		||||
                    Web3Wrapper.toBaseUnitAmount(10, 18),
 | 
			
		||||
                    MarketOperation.Sell,
 | 
			
		||||
                    {
 | 
			
		||||
                        ...DEFAULT_OPTS,
 | 
			
		||||
                        feeSchedule,
 | 
			
		||||
                        rfqt: {
 | 
			
		||||
                            isIndicative: false,
 | 
			
		||||
                            apiKey: 'foo',
 | 
			
		||||
@@ -1066,19 +1067,13 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            it('factors in fees for native orders', async () => {
 | 
			
		||||
                // Native orders will have the best rates but have fees,
 | 
			
		||||
                // dropping their effective rates.
 | 
			
		||||
                const nativeFeeRate = 0.06;
 | 
			
		||||
                const gasPrice = new BigNumber(1000e9);
 | 
			
		||||
                DEFAULT_DEX_GAS_USED = new BigNumber(100000);
 | 
			
		||||
                const rates: RatesBySource = {
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, 0.93, 0.92, 0.91]
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1],
 | 
			
		||||
                    [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1],
 | 
			
		||||
                    [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
 | 
			
		||||
                };
 | 
			
		||||
                const feeSchedule = {
 | 
			
		||||
                    [ERC20BridgeSource.Native]: _.constant(
 | 
			
		||||
                        FILL_AMOUNT.div(4)
 | 
			
		||||
                            .times(nativeFeeRate)
 | 
			
		||||
                            .dividedToIntegerBy(ETH_TO_MAKER_RATE),
 | 
			
		||||
                    ),
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [1, 0.96, 0.94, 0.92], // Effectively [0.98, 0.94, 0.92, 0.90]
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], // [0.95]
 | 
			
		||||
                    [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], // [0.94]
 | 
			
		||||
                    [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], // [0.094]
 | 
			
		||||
                };
 | 
			
		||||
                replaceSamplerOps({
 | 
			
		||||
                    getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
 | 
			
		||||
@@ -1088,7 +1083,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    marketOperationUtils,
 | 
			
		||||
                    createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
 | 
			
		||||
                    FILL_AMOUNT,
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, gasPrice },
 | 
			
		||||
                );
 | 
			
		||||
                const improvedOrders = improvedOrdersResponse.optimizedOrders;
 | 
			
		||||
                const orderSources = improvedOrders.map(o => o.fills[0].source);
 | 
			
		||||
@@ -1104,7 +1099,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            it('factors in fees for dexes', async () => {
 | 
			
		||||
                // Kyber will have the best rates but will have fees,
 | 
			
		||||
                // dropping its effective rates.
 | 
			
		||||
                const uniswapFeeRate = 0.2;
 | 
			
		||||
                const rates: RatesBySource = {
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
 | 
			
		||||
                    [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
 | 
			
		||||
@@ -1112,13 +1106,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    // Effectively [0.8, ~0.5, ~0, ~0]
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
 | 
			
		||||
                };
 | 
			
		||||
                const feeSchedule = {
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: _.constant(
 | 
			
		||||
                        FILL_AMOUNT.div(4)
 | 
			
		||||
                            .times(uniswapFeeRate)
 | 
			
		||||
                            .dividedToIntegerBy(ETH_TO_MAKER_RATE),
 | 
			
		||||
                    ),
 | 
			
		||||
                };
 | 
			
		||||
                replaceSamplerOps({
 | 
			
		||||
                    getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
 | 
			
		||||
                    getMedianSellRate: createGetMedianSellRate(ETH_TO_MAKER_RATE),
 | 
			
		||||
@@ -1127,7 +1114,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    marketOperationUtils,
 | 
			
		||||
                    createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
 | 
			
		||||
                    FILL_AMOUNT,
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4 },
 | 
			
		||||
                );
 | 
			
		||||
                const improvedOrders = improvedOrdersResponse.optimizedOrders;
 | 
			
		||||
                const orderSources = improvedOrders.map(o => o.fills[0].source);
 | 
			
		||||
@@ -1240,10 +1227,12 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    FILL_AMOUNT,
 | 
			
		||||
                    MarketOperation.Sell,
 | 
			
		||||
                    {
 | 
			
		||||
                        ...DEFAULT_GET_MARKET_ORDERS_OPTS,
 | 
			
		||||
                        includedSources: [ERC20BridgeSource.LiquidityProvider],
 | 
			
		||||
                        excludedSources: [],
 | 
			
		||||
                        numSamples: 4,
 | 
			
		||||
                        bridgeSlippage: 0,
 | 
			
		||||
                        gasPrice: GAS_PRICE,
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
                const result = ordersAndReport.optimizedOrders;
 | 
			
		||||
@@ -1280,7 +1269,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                });
 | 
			
		||||
                const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
 | 
			
		||||
                const gasPrice = 100e9; // 100 gwei
 | 
			
		||||
                const exchangeProxyOverhead = (sourceFlags: number) =>
 | 
			
		||||
                const exchangeProxyOverhead = (sourceFlags: bigint) =>
 | 
			
		||||
                    sourceFlags === SOURCE_FLAGS.LiquidityProvider
 | 
			
		||||
                        ? constants.ZERO_AMOUNT
 | 
			
		||||
                        : new BigNumber(1.3e5).times(gasPrice);
 | 
			
		||||
@@ -1313,15 +1302,14 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                FILL_AMOUNT,
 | 
			
		||||
                _.times(NUM_SAMPLES, () => DEFAULT_RATES[ERC20BridgeSource.Native][0]),
 | 
			
		||||
            );
 | 
			
		||||
            const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> = {
 | 
			
		||||
            const DEFAULT_OPTS: GetMarketOrdersOpts = {
 | 
			
		||||
                ...DEFAULT_GET_MARKET_ORDERS_OPTS,
 | 
			
		||||
                numSamples: NUM_SAMPLES,
 | 
			
		||||
                sampleDistributionBase: 1,
 | 
			
		||||
                bridgeSlippage: 0,
 | 
			
		||||
                maxFallbackSlippage: 100,
 | 
			
		||||
                excludedSources: DEFAULT_EXCLUDED,
 | 
			
		||||
                allowFallback: false,
 | 
			
		||||
                gasSchedule: {},
 | 
			
		||||
                feeSchedule: {},
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            beforeEach(() => {
 | 
			
		||||
@@ -1528,20 +1516,13 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            it('factors in fees for native orders', async () => {
 | 
			
		||||
                // Native orders will have the best rates but have fees,
 | 
			
		||||
                // dropping their effective rates.
 | 
			
		||||
                const nativeFeeRate = 0.06;
 | 
			
		||||
                const gasPrice = new BigNumber(1000e9);
 | 
			
		||||
                DEFAULT_DEX_GAS_USED = new BigNumber(100000);
 | 
			
		||||
                const rates: RatesBySource = {
 | 
			
		||||
                    ...ZERO_RATES,
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91]
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1],
 | 
			
		||||
                    [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1],
 | 
			
		||||
                    [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
 | 
			
		||||
                };
 | 
			
		||||
                const feeSchedule = {
 | 
			
		||||
                    [ERC20BridgeSource.Native]: _.constant(
 | 
			
		||||
                        FILL_AMOUNT.div(4)
 | 
			
		||||
                            .times(nativeFeeRate)
 | 
			
		||||
                            .dividedToIntegerBy(ETH_TO_TAKER_RATE),
 | 
			
		||||
                    ),
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [1, 0.96, 0.94, 0.92], // Effectively [0.98, 0.94, 0.92, 0.90]
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1], // [0.95]
 | 
			
		||||
                    [ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1], // [0.94]
 | 
			
		||||
                    [ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1], // [0.094]
 | 
			
		||||
                };
 | 
			
		||||
                replaceSamplerOps({
 | 
			
		||||
                    getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
 | 
			
		||||
@@ -1551,7 +1532,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    marketOperationUtils,
 | 
			
		||||
                    createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
 | 
			
		||||
                    FILL_AMOUNT,
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, gasPrice },
 | 
			
		||||
                );
 | 
			
		||||
                const improvedOrders = improvedOrdersResponse.optimizedOrders;
 | 
			
		||||
                const orderSources = improvedOrders.map(o => o.fills[0].source);
 | 
			
		||||
@@ -1567,7 +1548,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            it('factors in fees for dexes', async () => {
 | 
			
		||||
                // Uniswap will have the best rates but will have fees,
 | 
			
		||||
                // dropping its effective rates.
 | 
			
		||||
                const uniswapFeeRate = 0.2;
 | 
			
		||||
                const rates: RatesBySource = {
 | 
			
		||||
                    ...ZERO_RATES,
 | 
			
		||||
                    [ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
 | 
			
		||||
@@ -1575,13 +1555,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
 | 
			
		||||
                    [ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1],
 | 
			
		||||
                };
 | 
			
		||||
                const feeSchedule = {
 | 
			
		||||
                    [ERC20BridgeSource.Uniswap]: _.constant(
 | 
			
		||||
                        FILL_AMOUNT.div(4)
 | 
			
		||||
                            .times(uniswapFeeRate)
 | 
			
		||||
                            .dividedToIntegerBy(ETH_TO_TAKER_RATE),
 | 
			
		||||
                    ),
 | 
			
		||||
                };
 | 
			
		||||
                replaceSamplerOps({
 | 
			
		||||
                    getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
 | 
			
		||||
                    getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE),
 | 
			
		||||
@@ -1590,7 +1563,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                    marketOperationUtils,
 | 
			
		||||
                    createOrdersFromBuyRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
 | 
			
		||||
                    FILL_AMOUNT,
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4, feeSchedule },
 | 
			
		||||
                    { ...DEFAULT_OPTS, numSamples: 4 },
 | 
			
		||||
                );
 | 
			
		||||
                const improvedOrders = improvedOrdersResponse.optimizedOrders;
 | 
			
		||||
                const orderSources = improvedOrders.map(o => o.fills[0].source);
 | 
			
		||||
@@ -1663,7 +1636,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                });
 | 
			
		||||
                const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
 | 
			
		||||
                const gasPrice = 100e9; // 100 gwei
 | 
			
		||||
                const exchangeProxyOverhead = (sourceFlags: number) =>
 | 
			
		||||
                const exchangeProxyOverhead = (sourceFlags: bigint) =>
 | 
			
		||||
                    sourceFlags === SOURCE_FLAGS.LiquidityProvider
 | 
			
		||||
                        ? constants.ZERO_AMOUNT
 | 
			
		||||
                        : new BigNumber(1.3e5).times(gasPrice);
 | 
			
		||||
@@ -1726,9 +1699,6 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
            signature: SIGNATURE,
 | 
			
		||||
        };
 | 
			
		||||
        const orders = [smallOrder, largeOrder];
 | 
			
		||||
        const feeSchedule = {
 | 
			
		||||
            [ERC20BridgeSource.Native]: _.constant(2e5),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        it('penalizes native fill based on target amount when target is smaller', () => {
 | 
			
		||||
            const path = createFills({
 | 
			
		||||
@@ -1737,7 +1707,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                dexQuotes: [],
 | 
			
		||||
                targetInput: takerAmount.minus(1),
 | 
			
		||||
                outputAmountPerEth,
 | 
			
		||||
                feeSchedule,
 | 
			
		||||
                gasPrice: GAS_PRICE,
 | 
			
		||||
            });
 | 
			
		||||
            expect((path[0][0].fillData as NativeFillData).order.maker).to.eq(smallOrder.order.maker);
 | 
			
		||||
            expect(path[0][0].input).to.be.bignumber.eq(takerAmount.minus(1));
 | 
			
		||||
@@ -1750,7 +1720,7 @@ describe('MarketOperationUtils tests', () => {
 | 
			
		||||
                dexQuotes: [],
 | 
			
		||||
                targetInput: POSITIVE_INF,
 | 
			
		||||
                outputAmountPerEth,
 | 
			
		||||
                feeSchedule,
 | 
			
		||||
                gasPrice: GAS_PRICE,
 | 
			
		||||
            });
 | 
			
		||||
            expect((path[0][0].fillData as NativeFillData).order.maker).to.eq(largeOrder.order.maker);
 | 
			
		||||
            expect((path[0][1].fillData as NativeFillData).order.maker).to.eq(smallOrder.order.maker);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import 'mocha';
 | 
			
		||||
import * as TypeMoq from 'typemoq';
 | 
			
		||||
 | 
			
		||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../src/types';
 | 
			
		||||
import { NATIVE_LIMIT_ORDER_GAS_USED, NATIVE_RFQT_GAS_USED } from '../src/utils/market_operation_utils/constants';
 | 
			
		||||
import {
 | 
			
		||||
    CollapsedFill,
 | 
			
		||||
    DexSample,
 | 
			
		||||
@@ -34,6 +35,9 @@ import { getRandomAmount, getRandomSignature } from './utils/utils';
 | 
			
		||||
chaiSetup.configure();
 | 
			
		||||
const expect = chai.expect;
 | 
			
		||||
 | 
			
		||||
const ONE = new BigNumber(1);
 | 
			
		||||
const GAS_USED = ONE;
 | 
			
		||||
 | 
			
		||||
function collapsedFillFromNativeOrder(order: NativeOrderWithFillableAmounts): NativeCollapsedFill {
 | 
			
		||||
    const fillData = {
 | 
			
		||||
        order: order.order,
 | 
			
		||||
@@ -51,6 +55,8 @@ function collapsedFillFromNativeOrder(order: NativeOrderWithFillableAmounts): Na
 | 
			
		||||
                ? (fillData as NativeLimitOrderFillData)
 | 
			
		||||
                : (fillData as NativeRfqOrderFillData),
 | 
			
		||||
        subFills: [],
 | 
			
		||||
        gasUsed:
 | 
			
		||||
            order.type === FillQuoteTransformerOrderType.Limit ? NATIVE_LIMIT_ORDER_GAS_USED : NATIVE_RFQT_GAS_USED,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -63,12 +69,14 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            input: new BigNumber(10003),
 | 
			
		||||
            output: new BigNumber(10004),
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const uniswapSample2: DexSample = {
 | 
			
		||||
            source: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
            input: new BigNumber(10005),
 | 
			
		||||
            output: new BigNumber(10006),
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const orderbookOrder1: NativeOrderWithFillableAmounts = {
 | 
			
		||||
            order: new LimitOrder({ takerAmount: new BigNumber(1000) }),
 | 
			
		||||
@@ -161,6 +169,7 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            fillData: {
 | 
			
		||||
                order: rfqtOrder1.order,
 | 
			
		||||
            } as NativeRfqOrderFillData,
 | 
			
		||||
            gasUsed: NATIVE_RFQT_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const rfqtOrder2Source: NativeRfqOrderQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.Native,
 | 
			
		||||
@@ -173,6 +182,7 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            fillData: {
 | 
			
		||||
                order: rfqtOrder2.order,
 | 
			
		||||
            } as NativeRfqOrderFillData,
 | 
			
		||||
            gasUsed: NATIVE_RFQT_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const orderbookOrder2Source: NativeLimitOrderQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.Native,
 | 
			
		||||
@@ -183,18 +193,21 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            fillData: {
 | 
			
		||||
                order: orderbookOrder2.order,
 | 
			
		||||
            } as NativeLimitOrderFillData,
 | 
			
		||||
            gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const uniswap2Source: BridgeQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
            makerAmount: uniswapSample2.output,
 | 
			
		||||
            takerAmount: uniswapSample2.input,
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const kyber2Source: BridgeQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.Kyber,
 | 
			
		||||
            makerAmount: kyberSample2.output,
 | 
			
		||||
            takerAmount: kyberSample2.input,
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const expectedSourcesConsidered: QuoteReportEntry[] = [rfqtOrder1Source, rfqtOrder2Source];
 | 
			
		||||
@@ -215,12 +228,14 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            input: new BigNumber(10000),
 | 
			
		||||
            output: new BigNumber(10001),
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const uniswapSample1: DexSample = {
 | 
			
		||||
            source: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
            input: new BigNumber(10003),
 | 
			
		||||
            output: new BigNumber(10004),
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const orderbookOrder1: NativeOrderWithFillableAmounts = {
 | 
			
		||||
            order: new LimitOrder({ takerAmount: new BigNumber(1101) }),
 | 
			
		||||
@@ -267,18 +282,21 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            fillData: {
 | 
			
		||||
                order: orderbookOrder1.order,
 | 
			
		||||
            } as NativeLimitOrderFillData,
 | 
			
		||||
            gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const uniswap1Source: BridgeQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.UniswapV2,
 | 
			
		||||
            makerAmount: uniswapSample1.input,
 | 
			
		||||
            takerAmount: uniswapSample1.output,
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        const kyber1Source: BridgeQuoteReportEntry = {
 | 
			
		||||
            liquiditySource: ERC20BridgeSource.Kyber,
 | 
			
		||||
            makerAmount: kyberSample1.input,
 | 
			
		||||
            takerAmount: kyberSample1.output,
 | 
			
		||||
            fillData: {},
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // No order is considered here because only Native RFQ orders are considered.
 | 
			
		||||
@@ -303,15 +321,15 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
                source: ERC20BridgeSource.Balancer,
 | 
			
		||||
                fillData: {},
 | 
			
		||||
                encodeCall: () => '',
 | 
			
		||||
                handleCallResults: _callResults => [new BigNumber(1337)],
 | 
			
		||||
                handleRevert: _c => [],
 | 
			
		||||
                handleCallResults: _callResults => ({ gasUsed: [], samples: [new BigNumber(1337)] }),
 | 
			
		||||
                handleRevert: _c => ({ gasUsed: [], samples: [] }),
 | 
			
		||||
            },
 | 
			
		||||
            secondHopSource: {
 | 
			
		||||
                source: ERC20BridgeSource.Curve,
 | 
			
		||||
                fillData: {},
 | 
			
		||||
                encodeCall: () => '',
 | 
			
		||||
                handleCallResults: _callResults => [new BigNumber(1337)],
 | 
			
		||||
                handleRevert: _c => [],
 | 
			
		||||
                handleCallResults: _callResults => ({ gasUsed: [], samples: [new BigNumber(1337)] }),
 | 
			
		||||
                handleRevert: _c => ({ gasUsed: [], samples: [] }),
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        const twoHopSample: DexSample<MultiHopFillData> = {
 | 
			
		||||
@@ -319,6 +337,7 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            input: new BigNumber(3005),
 | 
			
		||||
            output: new BigNumber(3006),
 | 
			
		||||
            fillData: twoHopFillData,
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const orderReport = generateQuoteReport(marketOperation, [orderbookOrder1], twoHopSample);
 | 
			
		||||
@@ -328,6 +347,7 @@ describe('generateQuoteReport', async () => {
 | 
			
		||||
            takerAmount: twoHopSample.input,
 | 
			
		||||
            hopSources: [ERC20BridgeSource.Balancer, ERC20BridgeSource.Curve],
 | 
			
		||||
            fillData: twoHopFillData,
 | 
			
		||||
            gasUsed: GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // No entry is present in considered because No RFQ orders were reported.
 | 
			
		||||
@@ -356,12 +376,12 @@ function expectEqualQuoteReportEntries(
 | 
			
		||||
            expect(actualEntry.fillData.order).to.eql(
 | 
			
		||||
                // tslint:disable-next-line:no-unnecessary-type-assertion
 | 
			
		||||
                (expectedEntry.fillData as NativeFillData).order,
 | 
			
		||||
                `${variableName} incorrect at index ${idx}`,
 | 
			
		||||
                `${actualEntry.liquiditySource} ${variableName} incorrect at index ${idx}`,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        expect(_.omit(actualEntry, 'fillData')).to.eql(
 | 
			
		||||
            _.omit(expectedEntry, 'fillData'),
 | 
			
		||||
            `${variableName} incorrect at index ${idx}`,
 | 
			
		||||
            `${actualEntry.liquiditySource} ${variableName} incorrect at index ${idx}`,
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils';
 | 
			
		||||
import * as _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { MarketOperation } from '../src/types';
 | 
			
		||||
import { NATIVE_LIMIT_ORDER_GAS_USED } from '../src/utils/market_operation_utils/constants';
 | 
			
		||||
import {
 | 
			
		||||
    CollapsedFill,
 | 
			
		||||
    ERC20BridgeSource,
 | 
			
		||||
@@ -26,8 +27,6 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
    const ONE = new BigNumber(1);
 | 
			
		||||
    const MAKER_TOKEN = randomAddress();
 | 
			
		||||
    const TAKER_TOKEN = randomAddress();
 | 
			
		||||
    const GAS_SCHEDULE = { [ERC20BridgeSource.Uniswap]: _.constant(1), [ERC20BridgeSource.Native]: _.constant(1) };
 | 
			
		||||
 | 
			
		||||
    // Check if two numbers are within `maxError` error rate within each other.
 | 
			
		||||
    function assertRoughlyEquals(n1: BigNumber, n2: BigNumber, maxError: BigNumber | number = 1e-10): void {
 | 
			
		||||
        // |n2-n1| / max(|n1|, |n2|)
 | 
			
		||||
@@ -161,6 +160,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
            },
 | 
			
		||||
            type,
 | 
			
		||||
            fills: createOrderCollapsedFills(fillableInput, fillableOutput, fillsCount),
 | 
			
		||||
            gasUsed: NATIVE_LIMIT_ORDER_GAS_USED,
 | 
			
		||||
        };
 | 
			
		||||
        return order;
 | 
			
		||||
    }
 | 
			
		||||
@@ -182,6 +182,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    input: subFillInputs[j],
 | 
			
		||||
                    output: subFillOutputs[j],
 | 
			
		||||
                })),
 | 
			
		||||
                gasUsed: new BigNumber(1),
 | 
			
		||||
            };
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
@@ -247,7 +248,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    fillsCount,
 | 
			
		||||
                    count: 1,
 | 
			
		||||
                });
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(fillableInput);
 | 
			
		||||
@@ -269,7 +270,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    count: 1,
 | 
			
		||||
                });
 | 
			
		||||
                const inputFillAmount = fillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
 | 
			
		||||
@@ -295,7 +296,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    count: 1,
 | 
			
		||||
                });
 | 
			
		||||
                const inputFillAmount = fillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
 | 
			
		||||
@@ -318,7 +319,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    count: 1,
 | 
			
		||||
                });
 | 
			
		||||
                const inputFillAmount = fillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(fillableInput);
 | 
			
		||||
@@ -343,7 +344,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                });
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, totalFillableInput);
 | 
			
		||||
@@ -370,7 +371,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, inputFillAmount);
 | 
			
		||||
@@ -397,7 +398,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, totalFillableInput);
 | 
			
		||||
@@ -423,7 +424,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                });
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, fillableInput);
 | 
			
		||||
@@ -450,7 +451,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, inputFillAmount);
 | 
			
		||||
@@ -477,7 +478,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, fillableInput);
 | 
			
		||||
@@ -500,7 +501,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                    count: 1,
 | 
			
		||||
                    type: FillQuoteTransformerOrderType.Rfq,
 | 
			
		||||
                });
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(fillableInput);
 | 
			
		||||
@@ -516,7 +517,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const fillableInput = getRandomOrderSize();
 | 
			
		||||
                const fillableOutput = getRandomOrderSize();
 | 
			
		||||
                const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(fillableInput);
 | 
			
		||||
@@ -531,7 +532,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const fillableOutput = getRandomOrderSize();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
 | 
			
		||||
@@ -545,7 +546,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const fillableOutput = getRandomOrderSize();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                expect(totalFilledInput).to.bignumber.eq(fillableInput);
 | 
			
		||||
@@ -567,7 +568,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                });
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, totalFillableInput);
 | 
			
		||||
@@ -591,7 +592,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, inputFillAmount);
 | 
			
		||||
@@ -615,7 +616,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
 | 
			
		||||
                const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, totalFillableInput);
 | 
			
		||||
@@ -638,7 +639,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                });
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, fillableInput, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, fillableInput);
 | 
			
		||||
@@ -662,7 +663,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(2 / 3).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, inputFillAmount);
 | 
			
		||||
@@ -686,7 +687,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
 | 
			
		||||
                const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
 | 
			
		||||
                const inputFillAmount = fillableInput.times(3 / 2).integerValue();
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
 | 
			
		||||
                const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE);
 | 
			
		||||
                const totalFilledInput = result.input.plus(result.inputFee);
 | 
			
		||||
                const totalFilledOutput = result.output.plus(result.outputFee);
 | 
			
		||||
                assertRoughlyEquals(totalFilledInput, fillableInput);
 | 
			
		||||
@@ -744,7 +745,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            if (side === MarketOperation.Sell) {
 | 
			
		||||
                expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableOutput);
 | 
			
		||||
@@ -769,7 +770,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            expect(result.gas).to.eq(countCollapsedFills(orders));
 | 
			
		||||
            expect(result.protocolFeeAmount).to.bignumber.gt(orders.length);
 | 
			
		||||
@@ -801,7 +802,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: inputFillAmount,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            expect(result.gas).to.gt(0);
 | 
			
		||||
            expect(result.protocolFeeAmount).to.bignumber.gt(0);
 | 
			
		||||
@@ -835,7 +836,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: totalFillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            assertRoughlyEquals(result.takerAssetAmount, fillableInput);
 | 
			
		||||
@@ -865,7 +866,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: inputFillAmount,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            expect(result.gas).to.gt(0);
 | 
			
		||||
            expect(result.protocolFeeAmount).to.bignumber.gt(0);
 | 
			
		||||
@@ -893,7 +894,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            expect(result.gas).to.eq(countCollapsedFills(orders));
 | 
			
		||||
            expect(result.protocolFeeAmount).to.bignumber.gt(orders.length);
 | 
			
		||||
@@ -923,7 +924,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: inputFillAmount,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            expect(result.gas).to.gt(0);
 | 
			
		||||
            expect(result.protocolFeeAmount).to.bignumber.gt(0);
 | 
			
		||||
@@ -951,7 +952,7 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE, slippage },
 | 
			
		||||
                opts: { slippage },
 | 
			
		||||
            });
 | 
			
		||||
            if (side === MarketOperation.Sell) {
 | 
			
		||||
                const slippedOutput = fillableOutput.times(1 - slippage).integerValue();
 | 
			
		||||
@@ -982,14 +983,14 @@ describe('quote_simulation tests', async () => {
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE },
 | 
			
		||||
                opts: {},
 | 
			
		||||
            });
 | 
			
		||||
            const worstCase = simulateWorstCaseFill({
 | 
			
		||||
                orders,
 | 
			
		||||
                side,
 | 
			
		||||
                fillAmount: fillableInput,
 | 
			
		||||
                gasPrice: ONE,
 | 
			
		||||
                opts: { gasSchedule: GAS_SCHEDULE, slippage: orderSlippage },
 | 
			
		||||
                opts: { slippage: orderSlippage },
 | 
			
		||||
            });
 | 
			
		||||
            const bestPrice = bestCase.makerAssetAmount.div(bestCase.totalTakerAssetAmount);
 | 
			
		||||
            const worstPrice = worstCase.makerAssetAmount.div(worstCase.totalTakerAssetAmount);
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ export type GetOrderFillableAssetAmountHandler = (
 | 
			
		||||
    devUtilsAddress: string,
 | 
			
		||||
) => GetOrderFillableAssetAmountResult;
 | 
			
		||||
 | 
			
		||||
export type SampleResults = BigNumber[];
 | 
			
		||||
export type SampleResults = [BigNumber[], BigNumber[]];
 | 
			
		||||
export type SampleSellsUniswapHandler = (
 | 
			
		||||
    router: string,
 | 
			
		||||
    takerToken: string,
 | 
			
		||||
@@ -44,22 +44,22 @@ export type SampleSellsKyberHandler = (
 | 
			
		||||
    takerToken: string,
 | 
			
		||||
    makerToken: string,
 | 
			
		||||
    takerTokenAmounts: BigNumber[],
 | 
			
		||||
) => [string, string, SampleResults];
 | 
			
		||||
) => [string, string, BigNumber[], BigNumber[]];
 | 
			
		||||
export type SampleBuysKyberHandler = (
 | 
			
		||||
    reserveId: string,
 | 
			
		||||
    takerToken: string,
 | 
			
		||||
    makerToken: string,
 | 
			
		||||
    makerTokenAmounts: BigNumber[],
 | 
			
		||||
) => [string, SampleResults];
 | 
			
		||||
) => [string, string, BigNumber[], BigNumber[]];
 | 
			
		||||
export type SampleUniswapV2Handler = (router: string, path: string[], assetAmounts: BigNumber[]) => SampleResults;
 | 
			
		||||
export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
 | 
			
		||||
export type SampleBuysMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => BigNumber[];
 | 
			
		||||
export type SampleSellsLPHandler = (
 | 
			
		||||
    providerAddress: string,
 | 
			
		||||
    takerToken: string,
 | 
			
		||||
    makerToken: string,
 | 
			
		||||
    takerTokenAmounts: BigNumber[],
 | 
			
		||||
) => SampleResults;
 | 
			
		||||
export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => SampleResults;
 | 
			
		||||
export type SampleSellsMultihopHandler = (path: string[], takerTokenAmounts: BigNumber[]) => BigNumber[];
 | 
			
		||||
 | 
			
		||||
const DUMMY_PROVIDER = {
 | 
			
		||||
    sendAsync: (..._args: any[]): any => {
 | 
			
		||||
@@ -130,7 +130,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        takerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<[string, string, BigNumber[]]> {
 | 
			
		||||
    ): ContractTxFunctionObj<[string, string, BigNumber[], BigNumber[]]> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleSellsFromKyberNetwork,
 | 
			
		||||
            this._handlers.sampleSellsFromKyberNetwork,
 | 
			
		||||
@@ -146,7 +146,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        takerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleSellsFromEth2Dai,
 | 
			
		||||
            this._handlers.sampleSellsFromEth2Dai,
 | 
			
		||||
@@ -162,7 +162,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        takerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleSellsFromUniswap,
 | 
			
		||||
            this._handlers.sampleSellsFromUniswap,
 | 
			
		||||
@@ -177,7 +177,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        router: string,
 | 
			
		||||
        path: string[],
 | 
			
		||||
        takerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleSellsFromUniswapV2,
 | 
			
		||||
            this._handlers.sampleSellsFromUniswapV2,
 | 
			
		||||
@@ -192,7 +192,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        takerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleSellsFromLiquidityProvider,
 | 
			
		||||
            this._handlers.sampleSellsFromLiquidityProvider,
 | 
			
		||||
@@ -208,7 +208,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        makerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleBuysFromEth2Dai,
 | 
			
		||||
            this._handlers.sampleBuysFromEth2Dai,
 | 
			
		||||
@@ -224,7 +224,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        takerToken: string,
 | 
			
		||||
        makerToken: string,
 | 
			
		||||
        makerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleBuysFromUniswap,
 | 
			
		||||
            this._handlers.sampleBuysFromUniswap,
 | 
			
		||||
@@ -239,7 +239,7 @@ export class MockSamplerContract extends ERC20BridgeSamplerContract {
 | 
			
		||||
        router: string,
 | 
			
		||||
        path: string[],
 | 
			
		||||
        makerAssetAmounts: BigNumber[],
 | 
			
		||||
    ): ContractTxFunctionObj<BigNumber[]> {
 | 
			
		||||
    ): ContractTxFunctionObj<SampleResults> {
 | 
			
		||||
        return this._wrapCall(
 | 
			
		||||
            super.sampleBuysFromUniswapV2,
 | 
			
		||||
            this._handlers.sampleBuysFromUniswapV2,
 | 
			
		||||
 
 | 
			
		||||
@@ -3,28 +3,23 @@
 | 
			
		||||
 * Warning: This file is auto-generated by contracts-gen. Don't edit manually.
 | 
			
		||||
 * -----------------------------------------------------------------------------
 | 
			
		||||
 */
 | 
			
		||||
export * from '../test/generated-wrappers/approximate_buys';
 | 
			
		||||
export * from '../test/generated-wrappers/balance_checker';
 | 
			
		||||
export * from '../test/generated-wrappers/balancer_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/balancer_v2_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/bancor_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/curve_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/curve_v2_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/d_o_d_o_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/delegate_hacked_erc20';
 | 
			
		||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
 | 
			
		||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/eth2_dai_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/fake_taker';
 | 
			
		||||
export * from '../test/generated-wrappers/i_balancer';
 | 
			
		||||
export * from '../test/generated-wrappers/i_bancor';
 | 
			
		||||
export * from '../test/generated-wrappers/i_curve';
 | 
			
		||||
export * from '../test/generated-wrappers/gas_overhead';
 | 
			
		||||
export * from '../test/generated-wrappers/hacked_erc20';
 | 
			
		||||
export * from '../test/generated-wrappers/i_eth2_dai';
 | 
			
		||||
export * from '../test/generated-wrappers/i_kyber_network';
 | 
			
		||||
export * from '../test/generated-wrappers/i_m_stable';
 | 
			
		||||
export * from '../test/generated-wrappers/i_mooniswap';
 | 
			
		||||
export * from '../test/generated-wrappers/i_multi_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/i_shell';
 | 
			
		||||
export * from '../test/generated-wrappers/i_smoothy';
 | 
			
		||||
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
 | 
			
		||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
 | 
			
		||||
export * from '../test/generated-wrappers/kyber_dmm_sampler';
 | 
			
		||||
@@ -34,11 +29,9 @@ export * from '../test/generated-wrappers/liquidity_provider_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/m_stable_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/maker_p_s_m_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/mooniswap_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/multi_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/native_order_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/sampler_utils';
 | 
			
		||||
export * from '../test/generated-wrappers/shell_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/smoothy_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/swap_revert_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/test_native_order_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/two_hop_sampler';
 | 
			
		||||
 
 | 
			
		||||
@@ -4,30 +4,28 @@
 | 
			
		||||
    "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
 | 
			
		||||
    "files": [
 | 
			
		||||
        "generated-artifacts/BalanceChecker.json",
 | 
			
		||||
        "generated-artifacts/DelegateHackedERC20.json",
 | 
			
		||||
        "generated-artifacts/ERC20BridgeSampler.json",
 | 
			
		||||
        "generated-artifacts/FakeTaker.json",
 | 
			
		||||
        "test/generated-artifacts/ApproximateBuys.json",
 | 
			
		||||
        "generated-artifacts/GasOverhead.json",
 | 
			
		||||
        "generated-artifacts/HackedERC20.json",
 | 
			
		||||
        "test/generated-artifacts/BalanceChecker.json",
 | 
			
		||||
        "test/generated-artifacts/BalancerSampler.json",
 | 
			
		||||
        "test/generated-artifacts/BalancerV2Sampler.json",
 | 
			
		||||
        "test/generated-artifacts/BancorSampler.json",
 | 
			
		||||
        "test/generated-artifacts/CurveSampler.json",
 | 
			
		||||
        "test/generated-artifacts/CurveV2Sampler.json",
 | 
			
		||||
        "test/generated-artifacts/DODOSampler.json",
 | 
			
		||||
        "test/generated-artifacts/DODOV2Sampler.json",
 | 
			
		||||
        "test/generated-artifacts/DelegateHackedERC20.json",
 | 
			
		||||
        "test/generated-artifacts/DummyLiquidityProvider.json",
 | 
			
		||||
        "test/generated-artifacts/ERC20BridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/Eth2DaiSampler.json",
 | 
			
		||||
        "test/generated-artifacts/FakeTaker.json",
 | 
			
		||||
        "test/generated-artifacts/IBalancer.json",
 | 
			
		||||
        "test/generated-artifacts/IBancor.json",
 | 
			
		||||
        "test/generated-artifacts/ICurve.json",
 | 
			
		||||
        "test/generated-artifacts/GasOverhead.json",
 | 
			
		||||
        "test/generated-artifacts/HackedERC20.json",
 | 
			
		||||
        "test/generated-artifacts/IEth2Dai.json",
 | 
			
		||||
        "test/generated-artifacts/IKyberNetwork.json",
 | 
			
		||||
        "test/generated-artifacts/IMStable.json",
 | 
			
		||||
        "test/generated-artifacts/IMooniswap.json",
 | 
			
		||||
        "test/generated-artifacts/IMultiBridge.json",
 | 
			
		||||
        "test/generated-artifacts/IShell.json",
 | 
			
		||||
        "test/generated-artifacts/ISmoothy.json",
 | 
			
		||||
        "test/generated-artifacts/IUniswapExchangeQuotes.json",
 | 
			
		||||
        "test/generated-artifacts/IUniswapV2Router01.json",
 | 
			
		||||
        "test/generated-artifacts/KyberDmmSampler.json",
 | 
			
		||||
@@ -37,11 +35,9 @@
 | 
			
		||||
        "test/generated-artifacts/MStableSampler.json",
 | 
			
		||||
        "test/generated-artifacts/MakerPSMSampler.json",
 | 
			
		||||
        "test/generated-artifacts/MooniswapSampler.json",
 | 
			
		||||
        "test/generated-artifacts/MultiBridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/NativeOrderSampler.json",
 | 
			
		||||
        "test/generated-artifacts/SamplerUtils.json",
 | 
			
		||||
        "test/generated-artifacts/ShellSampler.json",
 | 
			
		||||
        "test/generated-artifacts/SmoothySampler.json",
 | 
			
		||||
        "test/generated-artifacts/SwapRevertSampler.json",
 | 
			
		||||
        "test/generated-artifacts/TestERC20BridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/TestNativeOrderSampler.json",
 | 
			
		||||
        "test/generated-artifacts/TwoHopSampler.json",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user