contract MyBancorNetwork is ContractIds {
    
    using SafeMath for uint256;
    
    BancorNetwork bancor = BancorNetwork(0x6690819Cb98c1211A8e38790d6cD48316Ed518Db);
    uint64 private constant MAX_CONVERSION_FEE = 1000000;

    /**
        @dev returns the expected return amount for converting a specific amount by following
        a given conversion path.
        notice that there is no support for circular paths.

        @param _path        conversion path, see conversion path format above
        @param _amount      amount to convert from (in the initial source token)

        @return expected conversion return amount and conversion fee
    */
    function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256, uint256) {
        IERC20Token fromToken;
        ISmartToken smartToken; 
        IERC20Token toToken;
        IBancorConverter converter;
        uint256 amount;
        uint256 fee;
        uint256 supply;
        uint256 balance;
        uint32 weight;
        ISmartToken prevSmartToken;
        IBancorFormula formula = IBancorFormula(bancor.registry().getAddress(ContractIds.BANCOR_FORMULA));

        amount = _amount;
        fromToken = _path[0];

        // iterate over the conversion path
        for (uint256 i = 1; i < _path.length; i += 2) {
            smartToken = ISmartToken(_path[i]);
            toToken = _path[i + 1];
            converter = IBancorConverter(smartToken.owner());

            if (toToken == smartToken) { // buy the smart token
                // check if the current smart token supply was changed in the previous iteration
                supply = smartToken == prevSmartToken ? supply : smartToken.totalSupply();

                // validate input
                require(getConnectorSaleEnabled(converter, fromToken));

                // calculate the amount & the conversion fee
                balance = converter.getConnectorBalance(fromToken);
                weight = getConnectorWeight(converter, fromToken);
                amount = formula.calculatePurchaseReturn(supply, balance, weight, amount);
                fee = amount.mul(converter.conversionFee()).div(MAX_CONVERSION_FEE);
                amount -= fee;

                // update the smart token supply for the next iteration
                supply = smartToken.totalSupply() + amount;
            }
            else if (fromToken == smartToken) { // sell the smart token
                // check if the current smart token supply was changed in the previous iteration
                supply = smartToken == prevSmartToken ? supply : smartToken.totalSupply();

                // calculate the amount & the conversion fee
                balance = converter.getConnectorBalance(toToken);
                weight = getConnectorWeight(converter, toToken);
                amount = formula.calculateSaleReturn(supply, balance, weight, amount);
                fee = amount.mul(converter.conversionFee()).div(MAX_CONVERSION_FEE);
                amount -= fee;

                // update the smart token supply for the next iteration
                supply = smartToken.totalSupply() - amount;
            }
            else { // cross connector conversion
                (amount, fee) = fixGetReturn(
                    converter,
                    abi.encodeWithSelector(
                        converter.getReturn.selector,
                        fromToken,
                        toToken,
                        amount
                    )
                );
            }

            prevSmartToken = smartToken;
            fromToken = toToken;
        }

        return (amount, fee);
    }
    
    function fixGetReturn(address destination, bytes data) internal returns(uint256 amount, uint256 fee) {
        bytes memory ret = new bytes(64);
        bool success;
        assembly {
            success := call(
                sub(gas, 34710),
                destination,
                0,
                add(data, 32),
                mload(data),
                add(ret, 32),
                64
            )
        }
        
        if (success) {
            assembly {
                amount := mload(add(ret, 32))
                fee := mload(add(ret, 64))
            }
        }
    }
    
    /**
        @dev returns true if connector sale is enabled

        @param _converter       converter contract address
        @param _connector       connector's address to read from

        @return true if connector sale is enabled, otherwise - false
    */
    function getConnectorSaleEnabled(IBancorConverter _converter, IERC20Token _connector) 
        private
        view
        returns(bool)
    {
        uint256 virtualBalance;
        uint32 weight;
        bool isVirtualBalanceEnabled;
        bool isSaleEnabled;
        bool isSet;
        (virtualBalance, weight, isVirtualBalanceEnabled, isSaleEnabled, isSet) = _converter.connectors(_connector);
        return isSaleEnabled;
    }
    
    /**
        @dev returns the connector weight

        @param _converter       converter contract address
        @param _connector       connector's address to read from

        @return connector's weight
    */
    function getConnectorWeight(IBancorConverter _converter, IERC20Token _connector) 
        private
        view
        returns(uint32)
    {
        uint256 virtualBalance;
        uint32 weight;
        bool isVirtualBalanceEnabled;
        bool isSaleEnabled;
        bool isSet;
        (virtualBalance, weight, isVirtualBalanceEnabled, isSaleEnabled, isSet) = _converter.connectors(_connector);
        return weight;
    }
}
function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256, uint256) {
    IERC20Token fromToken;
    ISmartToken smartToken; 
    IERC20Token toToken;
    IBancorConverter converter;
    uint256 amount;
    uint256 fee;
    uint256 supply;
    uint256 balance;
    uint32 weight;
    ISmartToken prevSmartToken;
    IBancorFormula formula = IBancorFormula(bancor.registry().getAddress(ContractIds.BANCOR_FORMULA));

    amount = _amount;
    fromToken = _path[0];

    // iterate over the conversion path
    for (uint256 i = 1; i < _path.length; i += 2) {
        smartToken = ISmartToken(_path[i]);
        toToken = _path[i + 1];
        converter = IBancorConverter(smartToken.owner());

        if (toToken == smartToken) { // buy the smart token
            // check if the current smart token supply was changed in the previous iteration
            supply = smartToken == prevSmartToken ? supply : smartToken.totalSupply();

            // validate input
            require(getConnectorSaleEnabled(converter, fromToken));

            // calculate the amount & the conversion fee
            balance = converter.getConnectorBalance(fromToken);
            weight = getConnectorWeight(converter, fromToken);
            amount = formula.calculatePurchaseReturn(supply, balance, weight, amount);
            fee = amount.mul(converter.conversionFee()).div(MAX_CONVERSION_FEE);
            amount -= fee;

            // update the smart token supply for the next iteration
            supply = smartToken.totalSupply() + amount;
        }
        else if (fromToken == smartToken) { // sell the smart token
            // check if the current smart token supply was changed in the previous iteration
            supply = smartToken == prevSmartToken ? supply : smartToken.totalSupply();

            // calculate the amount & the conversion fee
            balance = converter.getConnectorBalance(toToken);
            weight = getConnectorWeight(converter, toToken);
            amount = formula.calculateSaleReturn(supply, balance, weight, amount);
            fee = amount.mul(converter.conversionFee()).div(MAX_CONVERSION_FEE);
            amount -= fee;

            // update the smart token supply for the next iteration
            supply = smartToken.totalSupply() - amount;
        }
        else { // cross connector conversion
            (amount, fee) = fixGetReturn(
                converter,
                abi.encodeWithSelector(
                    converter.getReturn.selector,
                    fromToken,
                    toToken,
                    amount
                )
            );
        }

        prevSmartToken = smartToken;
        fromToken = toToken;
    }

    return (amount, fee);
}

/**
 @dev returns the expected return amount for converting a specific amount by following
 a given conversion path.
 notice that there is no support for circular paths.
 
 @param _path        conversion path, see conversion path format above
 @param _amount      amount to convert from (in the initial source token)
 
 @return expected conversion return amount and conversion fee
 */