@0x/contracts-asset-proxy: Update CurveBridge to support more varied curves.
				
					
				
			`@0x/contracts-erc20-bridge-sampler`: Refactor. `@0x/contracts-erc20-bridge-sampler`: Add support for more varied curves. `@0x/contracts-integrations`: Update curve bridge tests.
This commit is contained in:
		
				
					committed by
					
						
						Lawrence Forman
					
				
			
			
				
	
			
			
			
						parent
						
							74d9df2fb0
						
					
				
				
					commit
					4dac620156
				
			@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "3.5.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Update `CurveBridge` to support more varied curves",
 | 
			
		||||
                "pr": 2633
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "3.4.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,6 @@ import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "../interfaces/IERC20Bridge.sol";
 | 
			
		||||
import "../interfaces/ICurve.sol";
 | 
			
		||||
import "./MixinGasToken.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// solhint-disable not-rely-on-time
 | 
			
		||||
@@ -34,14 +33,14 @@ import "./MixinGasToken.sol";
 | 
			
		||||
contract CurveBridge is
 | 
			
		||||
    IERC20Bridge,
 | 
			
		||||
    IWallet,
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    MixinGasToken
 | 
			
		||||
    DeploymentConstants
 | 
			
		||||
{
 | 
			
		||||
    struct CurveBridgeData {
 | 
			
		||||
        address curveAddress;
 | 
			
		||||
        bytes4 exchangeFunctionSelector;
 | 
			
		||||
        address fromTokenAddress;
 | 
			
		||||
        int128 fromCoinIdx;
 | 
			
		||||
        int128 toCoinIdx;
 | 
			
		||||
        int128 version;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Callback for `ICurve`. Tries to buy `amount` of
 | 
			
		||||
@@ -62,39 +61,31 @@ contract CurveBridge is
 | 
			
		||||
        bytes calldata bridgeData
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        freesGasTokensFromCollector
 | 
			
		||||
        returns (bytes4 success)
 | 
			
		||||
    {
 | 
			
		||||
        // Decode the bridge data to get the Curve metadata.
 | 
			
		||||
        CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
 | 
			
		||||
 | 
			
		||||
        address fromTokenAddress = ICurve(data.curveAddress).underlying_coins(data.fromCoinIdx);
 | 
			
		||||
        require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
 | 
			
		||||
        uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
 | 
			
		||||
        require(toTokenAddress != data.fromTokenAddress, "CurveBridge/INVALID_PAIR");
 | 
			
		||||
        uint256 fromTokenBalance = IERC20Token(data.fromTokenAddress).balanceOf(address(this));
 | 
			
		||||
        // Grant an allowance to the exchange to spend `fromTokenAddress` token.
 | 
			
		||||
        LibERC20Token.approveIfBelow(fromTokenAddress, data.curveAddress, fromTokenBalance);
 | 
			
		||||
        LibERC20Token.approveIfBelow(data.fromTokenAddress, data.curveAddress, fromTokenBalance);
 | 
			
		||||
 | 
			
		||||
        // Try to sell all of this contract's `fromTokenAddress` token balance.
 | 
			
		||||
        if (data.version == 0) {
 | 
			
		||||
            ICurve(data.curveAddress).exchange_underlying(
 | 
			
		||||
                data.fromCoinIdx,
 | 
			
		||||
                data.toCoinIdx,
 | 
			
		||||
                // dx
 | 
			
		||||
                fromTokenBalance,
 | 
			
		||||
                // min dy
 | 
			
		||||
                amount,
 | 
			
		||||
                // expires
 | 
			
		||||
                block.timestamp + 1
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            ICurve(data.curveAddress).exchange_underlying(
 | 
			
		||||
        {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                data.curveAddress.call(abi.encodeWithSelector(
 | 
			
		||||
                    data.exchangeFunctionSelector,
 | 
			
		||||
                    data.fromCoinIdx,
 | 
			
		||||
                    data.toCoinIdx,
 | 
			
		||||
                    // dx
 | 
			
		||||
                    fromTokenBalance,
 | 
			
		||||
                    // min dy
 | 
			
		||||
                    amount
 | 
			
		||||
            );
 | 
			
		||||
                ));
 | 
			
		||||
            if (!didSucceed) {
 | 
			
		||||
                assembly { revert(add(resultData, 32), mload(resultData)) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
 | 
			
		||||
@@ -102,7 +93,7 @@ contract CurveBridge is
 | 
			
		||||
        LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
 | 
			
		||||
 | 
			
		||||
        emit ERC20BridgeTransfer(
 | 
			
		||||
            fromTokenAddress,
 | 
			
		||||
            data.fromTokenAddress,
 | 
			
		||||
            toTokenAddress,
 | 
			
		||||
            fromTokenBalance,
 | 
			
		||||
            toTokenBalance,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,22 +22,6 @@ pragma solidity ^0.5.9;
 | 
			
		||||
// solhint-disable func-name-mixedcase
 | 
			
		||||
interface ICurve {
 | 
			
		||||
 | 
			
		||||
    /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
 | 
			
		||||
    ///      This function exists on early versions of Curve (USDC/DAI)
 | 
			
		||||
    /// @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.
 | 
			
		||||
    /// @param deadline The time in seconds when this operation should expire.
 | 
			
		||||
    function exchange_underlying(
 | 
			
		||||
        int128 i,
 | 
			
		||||
        int128 j,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        uint256 minBuyAmount,
 | 
			
		||||
        uint256 deadline
 | 
			
		||||
    )
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    /// @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.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.8.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Refactor and support more varied curves",
 | 
			
		||||
                "pr": 2633
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "1.7.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										123
									
								
								contracts/erc20-bridge-sampler/contracts/src/ApproximateBuys.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								contracts/erc20-bridge-sampler/contracts/src/ApproximateBuys.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.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 = LibMath.getPartialAmountCeil(
 | 
			
		||||
                    makerTokenAmounts[i],
 | 
			
		||||
                    buyAmount,
 | 
			
		||||
                    sellAmount
 | 
			
		||||
                );
 | 
			
		||||
                sellAmount = LibMath.getPartialAmountCeil(
 | 
			
		||||
                    (ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
 | 
			
		||||
                    ONE_HUNDED_PERCENT_BPS,
 | 
			
		||||
                    sellAmount
 | 
			
		||||
                );
 | 
			
		||||
                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] = LibMath.getPartialAmountCeil(
 | 
			
		||||
                makerTokenAmounts[i],
 | 
			
		||||
                buyAmount,
 | 
			
		||||
                sellAmount
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										156
									
								
								contracts/erc20-bridge-sampler/contracts/src/CurveSampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								contracts/erc20-bridge-sampler/contracts/src/CurveSampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./ICurve.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract CurveSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
{
 | 
			
		||||
    /// @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 = 600e3; // 600k
 | 
			
		||||
 | 
			
		||||
    /// @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 takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromCurve(
 | 
			
		||||
        CurveInfo memory curveInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (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));
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @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 makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromCurve(
 | 
			
		||||
        CurveInfo memory curveInfo,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (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));
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            takerTokenAmounts[i] = sellAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										140
									
								
								contracts/erc20-bridge-sampler/contracts/src/Eth2DaiSampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								contracts/erc20-bridge-sampler/contracts/src/Eth2DaiSampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,140 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "./IEth2Dai.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract Eth2DaiSampler is
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    SamplerUtils
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Base gas limit for Eth2Dai calls.
 | 
			
		||||
    uint256 constant private ETH2DAI_CALL_GAS = 1000e3; // 1m
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Eth2Dai/Oasis.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromEth2Dai(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        IEth2Dai(0).getBuyAmount.selector,
 | 
			
		||||
                        makerToken,
 | 
			
		||||
                        takerToken,
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            } else{
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Eth2Dai/Oasis using a hop to an intermediate token.
 | 
			
		||||
    ///      I.e WBTC/DAI via ETH or WBTC/ETH via DAI
 | 
			
		||||
    /// @param takerToken Address of the taker token (what to sell).
 | 
			
		||||
    /// @param makerToken Address of the maker token (what to buy).
 | 
			
		||||
    /// @param intermediateToken Address of the token to hop to.
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromEth2DaiHop(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        address intermediateToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        if (makerToken == intermediateToken || takerToken == intermediateToken) {
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
        uint256[] memory intermediateAmounts = sampleSellsFromEth2Dai(
 | 
			
		||||
            takerToken,
 | 
			
		||||
            intermediateToken,
 | 
			
		||||
            takerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
        makerTokenAmounts = sampleSellsFromEth2Dai(
 | 
			
		||||
            intermediateToken,
 | 
			
		||||
            makerToken,
 | 
			
		||||
            intermediateAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Eth2Dai/Oasis.
 | 
			
		||||
    /// @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 takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromEth2Dai(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        IEth2Dai(0).getPayAmount.selector,
 | 
			
		||||
                        takerToken,
 | 
			
		||||
                        makerToken,
 | 
			
		||||
                        makerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 sellAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                sellAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            takerTokenAmounts[i] = sellAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,22 +22,6 @@ pragma solidity ^0.5.9;
 | 
			
		||||
// solhint-disable func-name-mixedcase
 | 
			
		||||
interface ICurve {
 | 
			
		||||
 | 
			
		||||
    /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
 | 
			
		||||
    ///      This function exists on early versions of Curve (USDC/DAI)
 | 
			
		||||
    /// @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.
 | 
			
		||||
    /// @param deadline The time in seconds when this operation should expire.
 | 
			
		||||
    function exchange_underlying(
 | 
			
		||||
        int128 i,
 | 
			
		||||
        int128 j,
 | 
			
		||||
        uint256 sellAmount,
 | 
			
		||||
        uint256 minBuyAmount,
 | 
			
		||||
        uint256 deadline
 | 
			
		||||
    )
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    /// @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.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,293 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface IERC20BridgeSampler {
 | 
			
		||||
 | 
			
		||||
    struct FakeBuyOptions {
 | 
			
		||||
        uint256 targetSlippageBps;
 | 
			
		||||
        uint256 maxIterations;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @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
 | 
			
		||||
        view
 | 
			
		||||
        returns (bytes[] memory callResults);
 | 
			
		||||
 | 
			
		||||
    /// @dev Queries the fillable taker asset amounts of native orders.
 | 
			
		||||
    /// @param orders Native orders to query.
 | 
			
		||||
    /// @param orderSignatures Signatures for each respective order in `orders`.
 | 
			
		||||
    /// @param devUtilsAddress Address to the DevUtils contract.
 | 
			
		||||
    /// @return orderFillableTakerAssetAmounts How much taker asset can be filled
 | 
			
		||||
    ///         by each order in `orders`.
 | 
			
		||||
    function getOrderFillableTakerAssetAmounts(
 | 
			
		||||
        LibOrder.Order[] calldata orders,
 | 
			
		||||
        bytes[] calldata orderSignatures,
 | 
			
		||||
        address devUtilsAddress
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory orderFillableTakerAssetAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Queries the fillable maker asset amounts of native orders.
 | 
			
		||||
    /// @param orders Native orders to query.
 | 
			
		||||
    /// @param orderSignatures Signatures for each respective order in `orders`.
 | 
			
		||||
    /// @param devUtilsAddress Address to the DevUtils contract.
 | 
			
		||||
    /// @return orderFillableMakerAssetAmounts How much maker asset can be filled
 | 
			
		||||
    ///         by each order in `orders`.
 | 
			
		||||
    function getOrderFillableMakerAssetAmounts(
 | 
			
		||||
        LibOrder.Order[] calldata orders,
 | 
			
		||||
        bytes[] calldata orderSignatures,
 | 
			
		||||
        address devUtilsAddress
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory orderFillableMakerAssetAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Kyber.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromKyberNetwork(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Kyber.
 | 
			
		||||
    /// @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.
 | 
			
		||||
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromKyberNetwork(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts,
 | 
			
		||||
        FakeBuyOptions calldata opts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Eth2Dai/Oasis.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromEth2Dai(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Uniswap.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswap(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @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 buy amount for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswap(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Eth2Dai/Oasis.
 | 
			
		||||
    /// @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 takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromEth2Dai(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Curve.
 | 
			
		||||
    /// @param curveAddress Address of the Curve contract.
 | 
			
		||||
    /// @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 sampleSellsFromCurve(
 | 
			
		||||
        address curveAddress,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Curve.
 | 
			
		||||
    /// @param curveAddress Address of the Curve contract.
 | 
			
		||||
    /// @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 sampleBuysFromCurve(
 | 
			
		||||
        address curveAddress,
 | 
			
		||||
        int128 fromTokenIdx,
 | 
			
		||||
        int128 toTokenIdx,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
    /// @param registryAddress Address of the liquidity provider registry 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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromLiquidityProviderRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @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[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
    /// @param registryAddress Address of the liquidity provider registry 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.
 | 
			
		||||
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromLiquidityProviderRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts,
 | 
			
		||||
        FakeBuyOptions calldata opts
 | 
			
		||||
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns the address of a liquidity provider for the given market
 | 
			
		||||
    ///      (takerToken, makerToken), from a registry of liquidity providers.
 | 
			
		||||
    ///      Returns address(0) if no such provider exists in the registry.
 | 
			
		||||
    /// @param takerToken Taker asset managed by liquidity provider.
 | 
			
		||||
    /// @param makerToken Maker asset managed by liquidity provider.
 | 
			
		||||
    /// @return providerAddress Address of the liquidity provider.
 | 
			
		||||
    function getLiquidityProviderFromRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address providerAddress);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from UniswapV2.
 | 
			
		||||
    /// @param path Token route.
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswapV2(
 | 
			
		||||
        address[] calldata path,
 | 
			
		||||
        uint256[] calldata takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts);
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from UniswapV2.
 | 
			
		||||
    /// @param path Token route.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswapV2(
 | 
			
		||||
        address[] calldata path,
 | 
			
		||||
        uint256[] calldata makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										218
									
								
								contracts/erc20-bridge-sampler/contracts/src/KyberSampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								contracts/erc20-bridge-sampler/contracts/src/KyberSampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "./IKyberNetwork.sol";
 | 
			
		||||
import "./IKyberNetworkProxy.sol";
 | 
			
		||||
import "./IKyberStorage.sol";
 | 
			
		||||
import "./IKyberHintHandler.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract KyberSampler is
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for Kyber calls.
 | 
			
		||||
    uint256 constant private KYBER_CALL_GAS = 1500e3; // 1.5m
 | 
			
		||||
    /// @dev The Kyber Uniswap Reserve address
 | 
			
		||||
    address constant private KYBER_UNISWAP_RESERVE = 0x31E085Afd48a1d6e51Cc193153d625e8f0514C7F;
 | 
			
		||||
    /// @dev The Kyber Uniswap V2 Reserve address
 | 
			
		||||
    address constant private KYBER_UNISWAPV2_RESERVE = 0x10908C875D865C66f271F5d3949848971c9595C9;
 | 
			
		||||
    /// @dev The Kyber Eth2Dai Reserve address
 | 
			
		||||
    address constant private KYBER_ETH2DAI_RESERVE = 0x1E158c0e93c30d24e918Ef83d1e0bE23595C3c0f;
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Kyber.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromKyberNetwork(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        address wethAddress = _getWethAddress();
 | 
			
		||||
        uint256 value;
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            if (takerToken == wethAddress || makerToken == wethAddress) {
 | 
			
		||||
                // Direct ETH based trade
 | 
			
		||||
                value = _sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts[i]);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Hop to ETH
 | 
			
		||||
                value = _sampleSellFromKyberNetwork(takerToken, wethAddress, takerTokenAmounts[i]);
 | 
			
		||||
                if (value != 0) {
 | 
			
		||||
                    value = _sampleSellFromKyberNetwork(wethAddress, makerToken, value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from Kyber.
 | 
			
		||||
    /// @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 takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromKyberNetwork(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        return _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromKyber(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (bool success, bytes memory resultData) =
 | 
			
		||||
            address(this).staticcall(abi.encodeWithSelector(
 | 
			
		||||
                this.sampleSellsFromKyberNetwork.selector,
 | 
			
		||||
                abi.decode(takerTokenData, (address)),
 | 
			
		||||
                abi.decode(makerTokenData, (address)),
 | 
			
		||||
                _toSingleValueArray(sellAmount)
 | 
			
		||||
            ));
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // solhint-disable-next-line indent
 | 
			
		||||
        return abi.decode(resultData, (uint256[]))[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _appendToList(bytes32[] memory list, bytes32 item) private view returns (bytes32[] memory appendedList)
 | 
			
		||||
    {
 | 
			
		||||
        appendedList = new bytes32[](list.length + 1);
 | 
			
		||||
        for (uint256 i = 0; i < list.length; i++) {
 | 
			
		||||
            appendedList[i] = list[i];
 | 
			
		||||
        }
 | 
			
		||||
        appendedList[appendedList.length - 1] = item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _getKyberAddresses()
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (IKyberHintHandler kyberHint, IKyberStorage kyberStorage)
 | 
			
		||||
    {
 | 
			
		||||
        (, , kyberHint, kyberStorage, ,) = IKyberNetwork(
 | 
			
		||||
            IKyberNetworkProxy(_getKyberNetworkProxyAddress()).kyberNetwork()).getContracts();
 | 
			
		||||
        return (IKyberHintHandler(kyberHint), IKyberStorage(kyberStorage));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellFromKyberNetwork(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256 takerTokenAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 makerTokenAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (IKyberHintHandler kyberHint, IKyberStorage kyberStorage) = _getKyberAddresses();
 | 
			
		||||
        // Ban reserves which can clash with our internal aggregation
 | 
			
		||||
        bytes32[] memory reserveIds = kyberStorage.getReserveIdsPerTokenSrc(
 | 
			
		||||
            takerToken == _getWethAddress() ? makerToken : takerToken
 | 
			
		||||
        );
 | 
			
		||||
        bytes32[] memory bannedReserveIds = new bytes32[](0);
 | 
			
		||||
        // Poor mans resize and append
 | 
			
		||||
        for (uint256 i = 0; i < reserveIds.length; i++) {
 | 
			
		||||
            if (
 | 
			
		||||
                reserveIds[i] == kyberStorage.getReserveId(KYBER_UNISWAP_RESERVE) ||
 | 
			
		||||
                reserveIds[i] == kyberStorage.getReserveId(KYBER_UNISWAPV2_RESERVE) ||
 | 
			
		||||
                reserveIds[i] == kyberStorage.getReserveId(KYBER_ETH2DAI_RESERVE)
 | 
			
		||||
            ) {
 | 
			
		||||
                bannedReserveIds = _appendToList(bannedReserveIds, reserveIds[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Sampler either detects X->ETH/ETH->X
 | 
			
		||||
        // or subsamples as X->ETH-Y. So token->token here is not possible
 | 
			
		||||
        bytes memory hint;
 | 
			
		||||
        if (takerToken == _getWethAddress()) {
 | 
			
		||||
            // ETH -> X
 | 
			
		||||
            hint = kyberHint.buildEthToTokenHint(
 | 
			
		||||
                    makerToken,
 | 
			
		||||
                    IKyberHintHandler.TradeType.MaskOut,
 | 
			
		||||
                    bannedReserveIds,
 | 
			
		||||
                    new uint256[](0));
 | 
			
		||||
        } else {
 | 
			
		||||
            // X->ETH
 | 
			
		||||
            hint = kyberHint.buildEthToTokenHint(
 | 
			
		||||
                    takerToken,
 | 
			
		||||
                    IKyberHintHandler.TradeType.MaskOut,
 | 
			
		||||
                    bannedReserveIds,
 | 
			
		||||
                    new uint256[](0));
 | 
			
		||||
        }
 | 
			
		||||
        (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
            _getKyberNetworkProxyAddress().staticcall.gas(KYBER_CALL_GAS)(
 | 
			
		||||
                abi.encodeWithSelector(
 | 
			
		||||
                    IKyberNetworkProxy(0).getExpectedRateAfterFee.selector,
 | 
			
		||||
                    takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken,
 | 
			
		||||
                    makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken,
 | 
			
		||||
                    takerTokenAmount,
 | 
			
		||||
                    0, // fee
 | 
			
		||||
                    hint
 | 
			
		||||
                ));
 | 
			
		||||
        uint256 rate = 0;
 | 
			
		||||
        if (didSucceed) {
 | 
			
		||||
            (rate) = abi.decode(resultData, (uint256));
 | 
			
		||||
        } else {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
 | 
			
		||||
        uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
 | 
			
		||||
        makerTokenAmount =
 | 
			
		||||
            rate *
 | 
			
		||||
            takerTokenAmount *
 | 
			
		||||
            10 ** makerTokenDecimals /
 | 
			
		||||
            10 ** takerTokenDecimals /
 | 
			
		||||
            10 ** 18;
 | 
			
		||||
        return makerTokenAmount;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,168 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "./ILiquidityProvider.sol";
 | 
			
		||||
import "./ILiquidityProviderRegistry.sol";
 | 
			
		||||
import "./ApproximateBuys.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract LiquidityProviderSampler is
 | 
			
		||||
    SamplerUtils,
 | 
			
		||||
    ApproximateBuys
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Default gas limit for liquidity provider calls.
 | 
			
		||||
    uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
    /// @param registryAddress Address of the liquidity provider registry 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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromLiquidityProviderRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        // Query registry for provider address.
 | 
			
		||||
        address providerAddress = getLiquidityProviderFromRegistry(
 | 
			
		||||
            registryAddress,
 | 
			
		||||
            takerToken,
 | 
			
		||||
            makerToken
 | 
			
		||||
        );
 | 
			
		||||
        // If provider doesn't exist, return all zeros.
 | 
			
		||||
        if (providerAddress == address(0)) {
 | 
			
		||||
            return makerTokenAmounts;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        ILiquidityProvider(0).getSellQuote.selector,
 | 
			
		||||
                        takerToken,
 | 
			
		||||
                        makerToken,
 | 
			
		||||
                        takerTokenAmounts[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256));
 | 
			
		||||
            } else {
 | 
			
		||||
                // Exit early if the amount is too high for the liquidity provider to serve
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
 | 
			
		||||
    /// @param registryAddress Address of the liquidity provider registry 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 takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromLiquidityProviderRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        return _sampleApproximateBuys(
 | 
			
		||||
            ApproximateBuyQuoteOpts({
 | 
			
		||||
                makerTokenData: abi.encode(makerToken, registryAddress),
 | 
			
		||||
                takerTokenData: abi.encode(takerToken, registryAddress),
 | 
			
		||||
                getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProviderRegistry
 | 
			
		||||
            }),
 | 
			
		||||
            makerTokenAmounts
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Returns the address of a liquidity provider for the given market
 | 
			
		||||
    ///      (takerToken, makerToken), from a registry of liquidity providers.
 | 
			
		||||
    ///      Returns address(0) if no such provider exists in the registry.
 | 
			
		||||
    /// @param takerToken Taker asset managed by liquidity provider.
 | 
			
		||||
    /// @param makerToken Maker asset managed by liquidity provider.
 | 
			
		||||
    /// @return providerAddress Address of the liquidity provider.
 | 
			
		||||
    function getLiquidityProviderFromRegistry(
 | 
			
		||||
        address registryAddress,
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (address providerAddress)
 | 
			
		||||
    {
 | 
			
		||||
        bytes memory callData = abi.encodeWithSelector(
 | 
			
		||||
            ILiquidityProviderRegistry(0).getLiquidityProviderForMarket.selector,
 | 
			
		||||
            takerToken,
 | 
			
		||||
            makerToken
 | 
			
		||||
        );
 | 
			
		||||
        (bool didSucceed, bytes memory returnData) = registryAddress.staticcall(callData);
 | 
			
		||||
        if (didSucceed && returnData.length == 32) {
 | 
			
		||||
            return LibBytes.readAddress(returnData, 12);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function _sampleSellForApproximateBuyFromLiquidityProviderRegistry(
 | 
			
		||||
        bytes memory takerTokenData,
 | 
			
		||||
        bytes memory makerTokenData,
 | 
			
		||||
        uint256 sellAmount
 | 
			
		||||
    )
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256 buyAmount)
 | 
			
		||||
    {
 | 
			
		||||
        (address takerToken, address plpRegistryAddress) =
 | 
			
		||||
            abi.decode(takerTokenData, (address, address));
 | 
			
		||||
        (address makerToken) =
 | 
			
		||||
            abi.decode(makerTokenData, (address));
 | 
			
		||||
        (bool success, bytes memory resultData) =
 | 
			
		||||
            address(this).staticcall(abi.encodeWithSelector(
 | 
			
		||||
                this.sampleSellsFromLiquidityProviderRegistry.selector,
 | 
			
		||||
                plpRegistryAddress,
 | 
			
		||||
                takerToken,
 | 
			
		||||
                makerToken,
 | 
			
		||||
                _toSingleValueArray(sellAmount)
 | 
			
		||||
            ));
 | 
			
		||||
        if (!success) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        // solhint-disable-next-line indent
 | 
			
		||||
        return abi.decode(resultData, (uint256[]))[0];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "./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));
 | 
			
		||||
            } else {
 | 
			
		||||
                // Exit early if the amount is too high for the liquidity provider to serve
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,125 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
 | 
			
		||||
import "./IDevUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract NativeOrderSampler {
 | 
			
		||||
 | 
			
		||||
    /// @dev Gas limit for DevUtils calls.
 | 
			
		||||
    uint256 constant internal DEV_UTILS_CALL_GAS = 500e3; // 500k
 | 
			
		||||
 | 
			
		||||
    /// @dev Queries the fillable taker asset amounts of native orders.
 | 
			
		||||
    ///      Effectively ignores orders that have empty signatures or
 | 
			
		||||
    ///      maker/taker asset amounts (returning 0).
 | 
			
		||||
    /// @param orders Native orders to query.
 | 
			
		||||
    /// @param orderSignatures Signatures for each respective order in `orders`.
 | 
			
		||||
    /// @param devUtilsAddress Address to the DevUtils contract.
 | 
			
		||||
    /// @return orderFillableTakerAssetAmounts How much taker asset can be filled
 | 
			
		||||
    ///         by each order in `orders`.
 | 
			
		||||
    function getOrderFillableTakerAssetAmounts(
 | 
			
		||||
        LibOrder.Order[] memory orders,
 | 
			
		||||
        bytes[] memory orderSignatures,
 | 
			
		||||
        address devUtilsAddress
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory orderFillableTakerAssetAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        orderFillableTakerAssetAmounts = new uint256[](orders.length);
 | 
			
		||||
        for (uint256 i = 0; i != orders.length; i++) {
 | 
			
		||||
            // Ignore orders with no signature or empty maker/taker amounts.
 | 
			
		||||
            if (orderSignatures[i].length == 0 ||
 | 
			
		||||
                orders[i].makerAssetAmount == 0 ||
 | 
			
		||||
                orders[i].takerAssetAmount == 0) {
 | 
			
		||||
                orderFillableTakerAssetAmounts[i] = 0;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            // solhint-disable indent
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                devUtilsAddress
 | 
			
		||||
                    .staticcall
 | 
			
		||||
                    .gas(DEV_UTILS_CALL_GAS)
 | 
			
		||||
                    (abi.encodeWithSelector(
 | 
			
		||||
                       IDevUtils(devUtilsAddress).getOrderRelevantState.selector,
 | 
			
		||||
                       orders[i],
 | 
			
		||||
                       orderSignatures[i]
 | 
			
		||||
                    ));
 | 
			
		||||
            // solhint-enable indent
 | 
			
		||||
            if (!didSucceed) {
 | 
			
		||||
                orderFillableTakerAssetAmounts[i] = 0;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            (
 | 
			
		||||
                LibOrder.OrderInfo memory orderInfo,
 | 
			
		||||
                uint256 fillableTakerAssetAmount,
 | 
			
		||||
                bool isValidSignature
 | 
			
		||||
            ) = abi.decode(
 | 
			
		||||
                resultData,
 | 
			
		||||
                (LibOrder.OrderInfo, uint256, bool)
 | 
			
		||||
            );
 | 
			
		||||
            // The fillable amount is zero if the order is not fillable or if the
 | 
			
		||||
            // signature is invalid.
 | 
			
		||||
            if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
 | 
			
		||||
                !isValidSignature) {
 | 
			
		||||
                orderFillableTakerAssetAmounts[i] = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                orderFillableTakerAssetAmounts[i] = fillableTakerAssetAmount;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Queries the fillable taker asset amounts of native orders.
 | 
			
		||||
    ///      Effectively ignores orders that have empty signatures or
 | 
			
		||||
    /// @param orders Native orders to query.
 | 
			
		||||
    /// @param orderSignatures Signatures for each respective order in `orders`.
 | 
			
		||||
    /// @param devUtilsAddress Address to the DevUtils contract.
 | 
			
		||||
    /// @return orderFillableMakerAssetAmounts How much maker asset can be filled
 | 
			
		||||
    ///         by each order in `orders`.
 | 
			
		||||
    function getOrderFillableMakerAssetAmounts(
 | 
			
		||||
        LibOrder.Order[] memory orders,
 | 
			
		||||
        bytes[] memory orderSignatures,
 | 
			
		||||
        address devUtilsAddress
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory orderFillableMakerAssetAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        orderFillableMakerAssetAmounts = getOrderFillableTakerAssetAmounts(
 | 
			
		||||
            orders,
 | 
			
		||||
            orderSignatures,
 | 
			
		||||
            devUtilsAddress
 | 
			
		||||
        );
 | 
			
		||||
        // `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
 | 
			
		||||
        // convert them to maker asset amounts.
 | 
			
		||||
        for (uint256 i = 0; i < orders.length; ++i) {
 | 
			
		||||
            if (orderFillableMakerAssetAmounts[i] != 0) {
 | 
			
		||||
                orderFillableMakerAssetAmounts[i] = LibMath.getPartialAmountCeil(
 | 
			
		||||
                    orderFillableMakerAssetAmounts[i],
 | 
			
		||||
                    orders[i].takerAssetAmount,
 | 
			
		||||
                    orders[i].makerAssetAmount
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.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)
 | 
			
		||||
        internal
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint8 decimals)
 | 
			
		||||
    {
 | 
			
		||||
        return LibERC20Token.decimals(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");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								contracts/erc20-bridge-sampler/contracts/src/UniswapSampler.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								contracts/erc20-bridge-sampler/contracts/src/UniswapSampler.sol
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-asset-proxy/contracts/src/interfaces/IUniswapExchangeFactory.sol";
 | 
			
		||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
 | 
			
		||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
 | 
			
		||||
import "./IUniswapExchangeQuotes.sol";
 | 
			
		||||
import "./SamplerUtils.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract UniswapSampler is
 | 
			
		||||
    DeploymentConstants,
 | 
			
		||||
    SamplerUtils
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for Uniswap calls.
 | 
			
		||||
    uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
 | 
			
		||||
    /// @dev Gas limit for UniswapV2 calls.
 | 
			
		||||
    uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from Uniswap.
 | 
			
		||||
    /// @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 makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswap(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
 | 
			
		||||
        IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            bool didSucceed = true;
 | 
			
		||||
            if (makerToken == _getWethAddress()) {
 | 
			
		||||
                (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(takerTokenExchange),
 | 
			
		||||
                    takerTokenExchange.getTokenToEthInputPrice.selector,
 | 
			
		||||
                    takerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else if (takerToken == _getWethAddress()) {
 | 
			
		||||
                (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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!didSucceed) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @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 takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswap(
 | 
			
		||||
        address takerToken,
 | 
			
		||||
        address makerToken,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        _assertValidPair(makerToken, takerToken);
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
 | 
			
		||||
        IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
 | 
			
		||||
            IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            bool didSucceed = true;
 | 
			
		||||
            if (makerToken == _getWethAddress()) {
 | 
			
		||||
                (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
 | 
			
		||||
                    address(takerTokenExchange),
 | 
			
		||||
                    takerTokenExchange.getTokenToEthOutputPrice.selector,
 | 
			
		||||
                    makerTokenAmounts[i]
 | 
			
		||||
                );
 | 
			
		||||
            } else if (takerToken == _getWethAddress()) {
 | 
			
		||||
                (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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!didSucceed) {
 | 
			
		||||
                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 tokenAddress Address of the token contract.
 | 
			
		||||
    /// @return exchange `IUniswapExchangeQuotes` for the token.
 | 
			
		||||
    function _getUniswapExchange(address tokenAddress)
 | 
			
		||||
        private
 | 
			
		||||
        view
 | 
			
		||||
        returns (IUniswapExchangeQuotes exchange)
 | 
			
		||||
    {
 | 
			
		||||
        exchange = IUniswapExchangeQuotes(
 | 
			
		||||
            address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
 | 
			
		||||
            .getExchange(tokenAddress))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,99 @@
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
  Copyright 2019 ZeroEx Intl.
 | 
			
		||||
 | 
			
		||||
  Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
  you may not use this file except in compliance with the License.
 | 
			
		||||
  You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
  Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
  distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
  See the License for the specific language governing permissions and
 | 
			
		||||
  limitations under the License.
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
pragma solidity ^0.5.9;
 | 
			
		||||
pragma experimental ABIEncoderV2;
 | 
			
		||||
 | 
			
		||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
 | 
			
		||||
import "./IUniswapV2Router01.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract UniswapV2Sampler is
 | 
			
		||||
    DeploymentConstants
 | 
			
		||||
{
 | 
			
		||||
    /// @dev Gas limit for UniswapV2 calls.
 | 
			
		||||
    uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample sell quotes from UniswapV2.
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken
 | 
			
		||||
    /// @param takerTokenAmounts Taker token sell amount for each sample.
 | 
			
		||||
    /// @return makerTokenAmounts Maker amounts bought at each taker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleSellsFromUniswapV2(
 | 
			
		||||
        address[] memory path,
 | 
			
		||||
        uint256[] memory takerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory makerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = takerTokenAmounts.length;
 | 
			
		||||
        makerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                _getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        IUniswapV2Router01(0).getAmountsOut.selector,
 | 
			
		||||
                        takerTokenAmounts[i],
 | 
			
		||||
                        path
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 buyAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                // solhint-disable-next-line indent
 | 
			
		||||
                buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1];
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            makerTokenAmounts[i] = buyAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// @dev Sample buy quotes from UniswapV2.
 | 
			
		||||
    /// @param path Token route. Should be takerToken -> makerToken.
 | 
			
		||||
    /// @param makerTokenAmounts Maker token buy amount for each sample.
 | 
			
		||||
    /// @return takerTokenAmounts Taker amounts sold at each maker token
 | 
			
		||||
    ///         amount.
 | 
			
		||||
    function sampleBuysFromUniswapV2(
 | 
			
		||||
        address[] memory path,
 | 
			
		||||
        uint256[] memory makerTokenAmounts
 | 
			
		||||
    )
 | 
			
		||||
        public
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256[] memory takerTokenAmounts)
 | 
			
		||||
    {
 | 
			
		||||
        uint256 numSamples = makerTokenAmounts.length;
 | 
			
		||||
        takerTokenAmounts = new uint256[](numSamples);
 | 
			
		||||
        for (uint256 i = 0; i < numSamples; i++) {
 | 
			
		||||
            (bool didSucceed, bytes memory resultData) =
 | 
			
		||||
                _getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
 | 
			
		||||
                    abi.encodeWithSelector(
 | 
			
		||||
                        IUniswapV2Router01(0).getAmountsIn.selector,
 | 
			
		||||
                        makerTokenAmounts[i],
 | 
			
		||||
                        path
 | 
			
		||||
                    ));
 | 
			
		||||
            uint256 sellAmount = 0;
 | 
			
		||||
            if (didSucceed) {
 | 
			
		||||
                // solhint-disable-next-line indent
 | 
			
		||||
                sellAmount = abi.decode(resultData, (uint256[]))[0];
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            takerTokenAmounts[i] = sellAmount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -36,9 +36,9 @@
 | 
			
		||||
        "compile:truffle": "truffle compile"
 | 
			
		||||
    },
 | 
			
		||||
    "config": {
 | 
			
		||||
        "publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
 | 
			
		||||
        "publicInterfaceContracts": "ERC20BridgeSampler,ILiquidityProvider,ILiquidityProviderRegistry,DummyLiquidityProviderRegistry,DummyLiquidityProvider",
 | 
			
		||||
        "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|TestERC20BridgeSampler).json"
 | 
			
		||||
        "abis": "./test/generated-artifacts/@(ApproximateBuys|CurveSampler|DummyLiquidityProvider|DummyLiquidityProviderRegistry|ERC20BridgeSampler|Eth2DaiSampler|ICurve|IDevUtils|IEth2Dai|IKyberHintHandler|IKyberNetwork|IKyberNetworkProxy|IKyberStorage|ILiquidityProvider|ILiquidityProviderRegistry|IMultiBridge|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|TestERC20BridgeSampler|UniswapSampler|UniswapV2Sampler).json"
 | 
			
		||||
    },
 | 
			
		||||
    "repository": {
 | 
			
		||||
        "type": "git",
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,10 @@ import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
import * as DummyLiquidityProvider from '../generated-artifacts/DummyLiquidityProvider.json';
 | 
			
		||||
import * as DummyLiquidityProviderRegistry from '../generated-artifacts/DummyLiquidityProviderRegistry.json';
 | 
			
		||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
 | 
			
		||||
import * as IERC20BridgeSampler from '../generated-artifacts/IERC20BridgeSampler.json';
 | 
			
		||||
import * as ILiquidityProvider from '../generated-artifacts/ILiquidityProvider.json';
 | 
			
		||||
import * as ILiquidityProviderRegistry from '../generated-artifacts/ILiquidityProviderRegistry.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    ILiquidityProvider: ILiquidityProvider as ContractArtifact,
 | 
			
		||||
    ILiquidityProviderRegistry: ILiquidityProviderRegistry as ContractArtifact,
 | 
			
		||||
    DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,5 @@
 | 
			
		||||
export * from '../generated-wrappers/dummy_liquidity_provider';
 | 
			
		||||
export * from '../generated-wrappers/dummy_liquidity_provider_registry';
 | 
			
		||||
export * from '../generated-wrappers/erc20_bridge_sampler';
 | 
			
		||||
export * from '../generated-wrappers/i_erc20_bridge_sampler';
 | 
			
		||||
export * from '../generated-wrappers/i_liquidity_provider';
 | 
			
		||||
export * from '../generated-wrappers/i_liquidity_provider_registry';
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,14 @@
 | 
			
		||||
 */
 | 
			
		||||
import { ContractArtifact } from 'ethereum-types';
 | 
			
		||||
 | 
			
		||||
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
 | 
			
		||||
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
 | 
			
		||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
 | 
			
		||||
import * as DummyLiquidityProviderRegistry from '../test/generated-artifacts/DummyLiquidityProviderRegistry.json';
 | 
			
		||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
 | 
			
		||||
import * as Eth2DaiSampler from '../test/generated-artifacts/Eth2DaiSampler.json';
 | 
			
		||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
 | 
			
		||||
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
 | 
			
		||||
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
 | 
			
		||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
 | 
			
		||||
import * as IKyberHintHandler from '../test/generated-artifacts/IKyberHintHandler.json';
 | 
			
		||||
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
 | 
			
		||||
@@ -21,14 +23,23 @@ import * as ILiquidityProviderRegistry from '../test/generated-artifacts/ILiquid
 | 
			
		||||
import * as IMultiBridge from '../test/generated-artifacts/IMultiBridge.json';
 | 
			
		||||
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
 | 
			
		||||
import * as IUniswapV2Router01 from '../test/generated-artifacts/IUniswapV2Router01.json';
 | 
			
		||||
import * as KyberSampler from '../test/generated-artifacts/KyberSampler.json';
 | 
			
		||||
import * as LiquidityProviderSampler from '../test/generated-artifacts/LiquidityProviderSampler.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 TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
 | 
			
		||||
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
 | 
			
		||||
import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.json';
 | 
			
		||||
export const artifacts = {
 | 
			
		||||
    ApproximateBuys: ApproximateBuys as ContractArtifact,
 | 
			
		||||
    CurveSampler: CurveSampler as ContractArtifact,
 | 
			
		||||
    DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
 | 
			
		||||
    DummyLiquidityProviderRegistry: DummyLiquidityProviderRegistry as ContractArtifact,
 | 
			
		||||
    ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    Eth2DaiSampler: Eth2DaiSampler as ContractArtifact,
 | 
			
		||||
    ICurve: ICurve as ContractArtifact,
 | 
			
		||||
    IDevUtils: IDevUtils as ContractArtifact,
 | 
			
		||||
    IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
    IEth2Dai: IEth2Dai as ContractArtifact,
 | 
			
		||||
    IKyberHintHandler: IKyberHintHandler as ContractArtifact,
 | 
			
		||||
    IKyberNetwork: IKyberNetwork as ContractArtifact,
 | 
			
		||||
@@ -39,5 +50,12 @@ export const artifacts = {
 | 
			
		||||
    IMultiBridge: IMultiBridge as ContractArtifact,
 | 
			
		||||
    IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
 | 
			
		||||
    IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
 | 
			
		||||
    KyberSampler: KyberSampler as ContractArtifact,
 | 
			
		||||
    LiquidityProviderSampler: LiquidityProviderSampler as ContractArtifact,
 | 
			
		||||
    MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
 | 
			
		||||
    NativeOrderSampler: NativeOrderSampler as ContractArtifact,
 | 
			
		||||
    SamplerUtils: SamplerUtils as ContractArtifact,
 | 
			
		||||
    UniswapSampler: UniswapSampler as ContractArtifact,
 | 
			
		||||
    UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
 | 
			
		||||
    TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -34,10 +34,6 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
    const MAKER_TOKEN = randomAddress();
 | 
			
		||||
    const TAKER_TOKEN = randomAddress();
 | 
			
		||||
    let devUtilsAddress: string;
 | 
			
		||||
    const FAKE_BUY_OPTS = {
 | 
			
		||||
        targetSlippageBps: new BigNumber(5),
 | 
			
		||||
        maxIterations: new BigNumber(5),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
 | 
			
		||||
@@ -443,14 +439,12 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('throws if tokens are the same', async () => {
 | 
			
		||||
            const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS).callAsync();
 | 
			
		||||
            const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, []).callAsync();
 | 
			
		||||
            return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('can return no quotes', async () => {
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            const quotes = await testContract.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, []).callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq([]);
 | 
			
		||||
        });
 | 
			
		||||
        const expectQuotesWithinRange = (
 | 
			
		||||
@@ -485,7 +479,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const [ethToMakerQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, MAKER_TOKEN, ['Kyber'], sampleAmounts);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], ethToMakerQuotes);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
 | 
			
		||||
        });
 | 
			
		||||
@@ -495,7 +489,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
@@ -504,7 +498,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
 | 
			
		||||
        });
 | 
			
		||||
@@ -514,7 +508,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
@@ -523,7 +517,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
 | 
			
		||||
            const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
 | 
			
		||||
        });
 | 
			
		||||
@@ -533,7 +527,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
            const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
 | 
			
		||||
            await enableFailTriggerAsync();
 | 
			
		||||
            const quotes = await testContract
 | 
			
		||||
                .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            expect(quotes).to.deep.eq(expectedQuotes);
 | 
			
		||||
        });
 | 
			
		||||
@@ -924,13 +918,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
 | 
			
		||||
        it('should be able to query buys from the liquidity provider', async () => {
 | 
			
		||||
            const result = await testContract
 | 
			
		||||
                .sampleBuysFromLiquidityProviderRegistry(
 | 
			
		||||
                    registryContract.address,
 | 
			
		||||
                    yAsset,
 | 
			
		||||
                    xAsset,
 | 
			
		||||
                    sampleAmounts,
 | 
			
		||||
                    FAKE_BUY_OPTS,
 | 
			
		||||
                )
 | 
			
		||||
                .sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            result.forEach((value, idx) => {
 | 
			
		||||
                expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
 | 
			
		||||
@@ -944,7 +932,6 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
                    yAsset,
 | 
			
		||||
                    randomAddress(),
 | 
			
		||||
                    sampleAmounts,
 | 
			
		||||
                    FAKE_BUY_OPTS,
 | 
			
		||||
                )
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            result.forEach(value => {
 | 
			
		||||
@@ -954,7 +941,7 @@ blockchainTests('erc20-bridge-sampler', env => {
 | 
			
		||||
 | 
			
		||||
        it('should just return zeros if the registry does not exist', async () => {
 | 
			
		||||
            const result = await testContract
 | 
			
		||||
                .sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts, FAKE_BUY_OPTS)
 | 
			
		||||
                .sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts)
 | 
			
		||||
                .callAsync();
 | 
			
		||||
            result.forEach(value => {
 | 
			
		||||
                expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,14 @@
 | 
			
		||||
 * 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/curve_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
 | 
			
		||||
export * from '../test/generated-wrappers/dummy_liquidity_provider_registry';
 | 
			
		||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/eth2_dai_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/i_curve';
 | 
			
		||||
export * from '../test/generated-wrappers/i_dev_utils';
 | 
			
		||||
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/i_eth2_dai';
 | 
			
		||||
export * from '../test/generated-wrappers/i_kyber_hint_handler';
 | 
			
		||||
export * from '../test/generated-wrappers/i_kyber_network';
 | 
			
		||||
@@ -19,4 +21,11 @@ export * from '../test/generated-wrappers/i_liquidity_provider_registry';
 | 
			
		||||
export * from '../test/generated-wrappers/i_multi_bridge';
 | 
			
		||||
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
 | 
			
		||||
export * from '../test/generated-wrappers/i_uniswap_v2_router01';
 | 
			
		||||
export * from '../test/generated-wrappers/kyber_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/liquidity_provider_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/test_erc20_bridge_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/uniswap_sampler';
 | 
			
		||||
export * from '../test/generated-wrappers/uniswap_v2_sampler';
 | 
			
		||||
 
 | 
			
		||||
@@ -6,15 +6,16 @@
 | 
			
		||||
        "generated-artifacts/DummyLiquidityProvider.json",
 | 
			
		||||
        "generated-artifacts/DummyLiquidityProviderRegistry.json",
 | 
			
		||||
        "generated-artifacts/ERC20BridgeSampler.json",
 | 
			
		||||
        "generated-artifacts/IERC20BridgeSampler.json",
 | 
			
		||||
        "generated-artifacts/ILiquidityProvider.json",
 | 
			
		||||
        "generated-artifacts/ILiquidityProviderRegistry.json",
 | 
			
		||||
        "test/generated-artifacts/ApproximateBuys.json",
 | 
			
		||||
        "test/generated-artifacts/CurveSampler.json",
 | 
			
		||||
        "test/generated-artifacts/DummyLiquidityProvider.json",
 | 
			
		||||
        "test/generated-artifacts/DummyLiquidityProviderRegistry.json",
 | 
			
		||||
        "test/generated-artifacts/ERC20BridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/Eth2DaiSampler.json",
 | 
			
		||||
        "test/generated-artifacts/ICurve.json",
 | 
			
		||||
        "test/generated-artifacts/IDevUtils.json",
 | 
			
		||||
        "test/generated-artifacts/IERC20BridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/IEth2Dai.json",
 | 
			
		||||
        "test/generated-artifacts/IKyberHintHandler.json",
 | 
			
		||||
        "test/generated-artifacts/IKyberNetwork.json",
 | 
			
		||||
@@ -25,7 +26,14 @@
 | 
			
		||||
        "test/generated-artifacts/IMultiBridge.json",
 | 
			
		||||
        "test/generated-artifacts/IUniswapExchangeQuotes.json",
 | 
			
		||||
        "test/generated-artifacts/IUniswapV2Router01.json",
 | 
			
		||||
        "test/generated-artifacts/TestERC20BridgeSampler.json"
 | 
			
		||||
        "test/generated-artifacts/KyberSampler.json",
 | 
			
		||||
        "test/generated-artifacts/LiquidityProviderSampler.json",
 | 
			
		||||
        "test/generated-artifacts/MultiBridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/NativeOrderSampler.json",
 | 
			
		||||
        "test/generated-artifacts/SamplerUtils.json",
 | 
			
		||||
        "test/generated-artifacts/TestERC20BridgeSampler.json",
 | 
			
		||||
        "test/generated-artifacts/UniswapSampler.json",
 | 
			
		||||
        "test/generated-artifacts/UniswapV2Sampler.json"
 | 
			
		||||
    ],
 | 
			
		||||
    "exclude": ["./deploy/solc/solc_bin"]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,13 @@
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "version": "2.7.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
            {
 | 
			
		||||
                "note": "Update curveBridge tests",
 | 
			
		||||
                "pr": 2633
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "version": "2.6.0",
 | 
			
		||||
        "changes": [
 | 
			
		||||
 
 | 
			
		||||
@@ -102,6 +102,7 @@
 | 
			
		||||
        "@0x/contracts-multisig": "^4.1.7",
 | 
			
		||||
        "@0x/contracts-staking": "^2.0.14",
 | 
			
		||||
        "@0x/contracts-test-utils": "^5.3.4",
 | 
			
		||||
        "@0x/subproviders": "^6.1.1",
 | 
			
		||||
        "@0x/types": "^3.2.0",
 | 
			
		||||
        "@0x/typescript-typings": "^5.1.1",
 | 
			
		||||
        "@0x/utils": "^5.5.1",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,11 @@
 | 
			
		||||
import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridge-sampler';
 | 
			
		||||
import { blockchainTests, describe, expect, toBaseUnitAmount, Web3ProviderEngine } from '@0x/contracts-test-utils';
 | 
			
		||||
import { RPCSubprovider } from '@0x/dev-utils/node_modules/@0x/subproviders';
 | 
			
		||||
import { RPCSubprovider } from '@0x/subproviders';
 | 
			
		||||
import { BigNumber, providerUtils } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
 | 
			
		||||
blockchainTests.configure({
 | 
			
		||||
    fork: {
 | 
			
		||||
        unlockedAccounts: [VB],
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
blockchainTests.skip('Mainnet Sampler Tests', env => {
 | 
			
		||||
    let testContract: ERC20BridgeSamplerContract;
 | 
			
		||||
    const fakeSamplerAddress = '0x1111111111111111111111111111111111111111';
 | 
			
		||||
    const overrides = {
 | 
			
		||||
@@ -21,7 +16,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        const provider = new Web3ProviderEngine();
 | 
			
		||||
        // tslint:disable-next-line:no-non-null-assertion
 | 
			
		||||
        provider.addProvider(new RPCSubprovider(process.env.FORK_RPC_URL!));
 | 
			
		||||
        provider.addProvider(new RPCSubprovider(process.env.RPC_URL!));
 | 
			
		||||
        providerUtils.startProviderEngine(provider);
 | 
			
		||||
        testContract = new ERC20BridgeSamplerContract(fakeSamplerAddress, provider, {
 | 
			
		||||
            ...env.txDefaults,
 | 
			
		||||
@@ -29,14 +24,19 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    describe('Curve', () => {
 | 
			
		||||
        const CURVE_ADDRESS = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56';
 | 
			
		||||
        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 samples = await testContract
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -44,7 +44,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
 | 
			
		||||
            it('samples sells from Curve USDC->DAI', async () => {
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .sampleSellsFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -56,7 +56,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
                // From DAI to USDC
 | 
			
		||||
                // I want to buy 1 USDC
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_INFO, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -66,7 +66,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
                // From USDC to DAI
 | 
			
		||||
                // I want to buy 1 DAI
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .sampleBuysFromCurve(CURVE_INFO, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -74,10 +74,6 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    describe('Kyber', () => {
 | 
			
		||||
        const FAKE_BUY_OPTS = {
 | 
			
		||||
            targetSlippageBps: new BigNumber(5),
 | 
			
		||||
            maxIterations: new BigNumber(5),
 | 
			
		||||
        };
 | 
			
		||||
        const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
 | 
			
		||||
        const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f';
 | 
			
		||||
        const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
 | 
			
		||||
@@ -110,7 +106,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
                // From ETH to DAI
 | 
			
		||||
                // I want to buy 1 DAI
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromKyberNetwork(WETH, DAI, [toBaseUnitAmount(1)], FAKE_BUY_OPTS)
 | 
			
		||||
                    .sampleBuysFromKyberNetwork(WETH, DAI, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
@@ -120,7 +116,7 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
 | 
			
		||||
                // From USDC to DAI
 | 
			
		||||
                // I want to buy 1 WETH
 | 
			
		||||
                const samples = await testContract
 | 
			
		||||
                    .sampleBuysFromKyberNetwork(DAI, WETH, [toBaseUnitAmount(1)], FAKE_BUY_OPTS)
 | 
			
		||||
                    .sampleBuysFromKyberNetwork(DAI, WETH, [toBaseUnitAmount(1)])
 | 
			
		||||
                    .callAsync({ overrides });
 | 
			
		||||
                expect(samples.length).to.be.bignumber.greaterThan(0);
 | 
			
		||||
                expect(samples[0]).to.be.bignumber.greaterThan(0);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,107 +4,128 @@ import { ERC20TokenContract } from '@0x/contracts-erc20';
 | 
			
		||||
import { blockchainTests, constants, describe, toBaseUnitAmount } from '@0x/contracts-test-utils';
 | 
			
		||||
import { AbiEncoder } from '@0x/utils';
 | 
			
		||||
 | 
			
		||||
const USDC_WALLET = '0xF977814e90dA44bFA03b6295A0616a897441aceC';
 | 
			
		||||
const DAI_WALLET = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
 | 
			
		||||
const WBTC_WALLET = '0x56178a0d5F301bAf6CF3e1Cd53d9863437345Bf9';
 | 
			
		||||
blockchainTests.configure({
 | 
			
		||||
    fork: {
 | 
			
		||||
        unlockedAccounts: [USDC_WALLET, DAI_WALLET, WBTC_WALLET],
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
blockchainTests.fork.resets('Mainnet curve bridge tests', env => {
 | 
			
		||||
    let testContract: CurveBridgeContract;
 | 
			
		||||
    const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
 | 
			
		||||
    const usdcWallet = '0xF977814e90dA44bFA03b6295A0616a897441aceC';
 | 
			
		||||
    const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
 | 
			
		||||
    const daiWallet = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
 | 
			
		||||
    const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
 | 
			
		||||
    const curveAddressUsdcDai = '0x2e60CF74d81ac34eB21eEff58Db4D385920ef419';
 | 
			
		||||
    const curveAddressUsdcDaiUsdt = '0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C';
 | 
			
		||||
    const daiTokenIdx = 0;
 | 
			
		||||
    const usdcTokenIdx = 1;
 | 
			
		||||
    const RECEIVER = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
 | 
			
		||||
    const bridgeDataEncoder = AbiEncoder.create([
 | 
			
		||||
        { name: 'curveAddress', type: 'address' },
 | 
			
		||||
        { name: 'exchangeFunctionSelector', type: 'bytes4' },
 | 
			
		||||
        { name: 'fromTokenAddress', type: 'address' },
 | 
			
		||||
        { name: 'fromTokenIdx', type: 'int128' },
 | 
			
		||||
        { name: 'toTokenIdx', type: 'int128' },
 | 
			
		||||
        { name: 'version', type: 'int128' },
 | 
			
		||||
    ]);
 | 
			
		||||
    before(async () => {
 | 
			
		||||
        testContract = await CurveBridgeContract.deployFrom0xArtifactAsync(
 | 
			
		||||
            assetProxyArtifacts.CurveBridge,
 | 
			
		||||
            env.provider,
 | 
			
		||||
            { ...env.txDefaults, from: daiWallet },
 | 
			
		||||
            { ...env.txDefaults },
 | 
			
		||||
            {},
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    describe('bridgeTransferFrom()', () => {
 | 
			
		||||
        describe('Version 0', () => {
 | 
			
		||||
            const version = 0;
 | 
			
		||||
        describe('exchange_underlying()', () => {
 | 
			
		||||
            const USDC_ADDRESS = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
 | 
			
		||||
            const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
 | 
			
		||||
            const DAI_TOKEN_IDX = 0;
 | 
			
		||||
            const USDC_TOKEN_IDX = 1;
 | 
			
		||||
            const EXCHANGE_UNDERLYING_SELECTOR = '0xa6417ed6';
 | 
			
		||||
            const CURVE_ADDRESS = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
 | 
			
		||||
            it('succeeds exchanges DAI for USDC', async () => {
 | 
			
		||||
                const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, daiTokenIdx, usdcTokenIdx, version]);
 | 
			
		||||
                const bridgeData = bridgeDataEncoder.encode([
 | 
			
		||||
                    CURVE_ADDRESS,
 | 
			
		||||
                    EXCHANGE_UNDERLYING_SELECTOR,
 | 
			
		||||
                    DAI_ADDRESS,
 | 
			
		||||
                    DAI_TOKEN_IDX,
 | 
			
		||||
                    USDC_TOKEN_IDX,
 | 
			
		||||
                ]);
 | 
			
		||||
                // Fund the Bridge
 | 
			
		||||
                const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
 | 
			
		||||
                const dai = new ERC20TokenContract(DAI_ADDRESS, env.provider, { ...env.txDefaults, from: DAI_WALLET });
 | 
			
		||||
                await dai
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
 | 
			
		||||
                    .awaitTransactionSuccessAsync({}, { shouldValidate: false });
 | 
			
		||||
                // Exchange via Curve
 | 
			
		||||
                await testContract
 | 
			
		||||
                    .bridgeTransferFrom(
 | 
			
		||||
                        usdcAddress,
 | 
			
		||||
                        USDC_ADDRESS,
 | 
			
		||||
                        constants.NULL_ADDRESS,
 | 
			
		||||
                        receiver,
 | 
			
		||||
                        RECEIVER,
 | 
			
		||||
                        constants.ZERO_AMOUNT,
 | 
			
		||||
                        bridgeData,
 | 
			
		||||
                    )
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
 | 
			
		||||
                    .awaitTransactionSuccessAsync({}, { shouldValidate: false });
 | 
			
		||||
            });
 | 
			
		||||
            it('succeeds exchanges USDC for DAI', async () => {
 | 
			
		||||
                const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, usdcTokenIdx, daiTokenIdx, version]);
 | 
			
		||||
                // Fund the Bridge
 | 
			
		||||
                const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
 | 
			
		||||
                await usdc
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1, 6))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
 | 
			
		||||
                // Exchange via Curve
 | 
			
		||||
                await testContract
 | 
			
		||||
                    .bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        describe('Version 1', () => {
 | 
			
		||||
            const version = 1;
 | 
			
		||||
            it('succeeds exchanges DAI for USDC', async () => {
 | 
			
		||||
                const bridgeData = bridgeDataEncoder.encode([
 | 
			
		||||
                    curveAddressUsdcDaiUsdt,
 | 
			
		||||
                    daiTokenIdx,
 | 
			
		||||
                    usdcTokenIdx,
 | 
			
		||||
                    version,
 | 
			
		||||
                    CURVE_ADDRESS,
 | 
			
		||||
                    EXCHANGE_UNDERLYING_SELECTOR,
 | 
			
		||||
                    USDC_ADDRESS,
 | 
			
		||||
                    USDC_TOKEN_IDX,
 | 
			
		||||
                    DAI_TOKEN_IDX,
 | 
			
		||||
                ]);
 | 
			
		||||
                // Fund the Bridge
 | 
			
		||||
                const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
 | 
			
		||||
                await dai
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
 | 
			
		||||
                const usdc = new ERC20TokenContract(USDC_ADDRESS, env.provider, {
 | 
			
		||||
                    ...env.txDefaults,
 | 
			
		||||
                    from: USDC_WALLET,
 | 
			
		||||
                });
 | 
			
		||||
                await usdc
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1, 6))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({}, { shouldValidate: false });
 | 
			
		||||
                // Exchange via Curve
 | 
			
		||||
                await testContract
 | 
			
		||||
                    .bridgeTransferFrom(
 | 
			
		||||
                        usdcAddress,
 | 
			
		||||
                        DAI_ADDRESS,
 | 
			
		||||
                        constants.NULL_ADDRESS,
 | 
			
		||||
                        receiver,
 | 
			
		||||
                        RECEIVER,
 | 
			
		||||
                        constants.ZERO_AMOUNT,
 | 
			
		||||
                        bridgeData,
 | 
			
		||||
                    )
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
 | 
			
		||||
                    .awaitTransactionSuccessAsync({}, { shouldValidate: false });
 | 
			
		||||
            });
 | 
			
		||||
            it('succeeds exchanges USDC for DAI', async () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        describe('exchange()', () => {
 | 
			
		||||
            const WBTC_ADDRESS = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599';
 | 
			
		||||
            const RENBTC_ADDRESS = '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d';
 | 
			
		||||
            const RENBTC_TOKEN_IDX = 0;
 | 
			
		||||
            const WBTC_TOKEN_IDX = 1;
 | 
			
		||||
            const EXCHANGE_SELECTOR = '0x3df02124';
 | 
			
		||||
            const CURVE_ADDRESS = '0x7fc77b5c7614e1533320ea6ddc2eb61fa00a9714';
 | 
			
		||||
            it('succeeds exchanges WBTC for renBTC', async () => {
 | 
			
		||||
                const bridgeData = bridgeDataEncoder.encode([
 | 
			
		||||
                    curveAddressUsdcDaiUsdt,
 | 
			
		||||
                    usdcTokenIdx,
 | 
			
		||||
                    daiTokenIdx,
 | 
			
		||||
                    version,
 | 
			
		||||
                    CURVE_ADDRESS,
 | 
			
		||||
                    EXCHANGE_SELECTOR,
 | 
			
		||||
                    WBTC_ADDRESS,
 | 
			
		||||
                    WBTC_TOKEN_IDX,
 | 
			
		||||
                    RENBTC_TOKEN_IDX,
 | 
			
		||||
                ]);
 | 
			
		||||
                // Fund the Bridge
 | 
			
		||||
                const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
 | 
			
		||||
                await usdc
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1, 6))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
 | 
			
		||||
                const wbtc = new ERC20TokenContract(WBTC_ADDRESS, env.provider, {
 | 
			
		||||
                    ...env.txDefaults,
 | 
			
		||||
                    from: WBTC_WALLET,
 | 
			
		||||
                });
 | 
			
		||||
                await wbtc
 | 
			
		||||
                    .transfer(testContract.address, toBaseUnitAmount(1, 8))
 | 
			
		||||
                    .awaitTransactionSuccessAsync({}, { shouldValidate: false });
 | 
			
		||||
                // Exchange via Curve
 | 
			
		||||
                await testContract
 | 
			
		||||
                    .bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
 | 
			
		||||
                    .bridgeTransferFrom(
 | 
			
		||||
                        RENBTC_ADDRESS,
 | 
			
		||||
                        constants.NULL_ADDRESS,
 | 
			
		||||
                        RECEIVER,
 | 
			
		||||
                        constants.ZERO_AMOUNT,
 | 
			
		||||
                        bridgeData,
 | 
			
		||||
                    )
 | 
			
		||||
                    .awaitTransactionSuccessAsync({ gas: 6e6 }, { shouldValidate: false });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user