contract BancorXHelper is ContractRegistryClient {
    constructor(IContractRegistry _registry) ContractRegistryClient(_registry) public {
    }

    /**
      * @dev converts any other token to BNT in the bancor network
      * by following a predefined conversion path and transfers the resulting
      * tokens to BancorX.
      * note that the contract should already have been given allowance for the source token (if not ETH)
      * 
      * @param _path                conversion path, see conversion path format above
      * @param _amount              amount to convert from (in the initial source token)
      * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
      * @param _toBlockchain        blockchain BNT will be issued on
      * @param _to                  address/account on _toBlockchain to send the BNT to
      * @param _conversionId        pre-determined unique (if non zero) id which refers to this transaction 
      * @param _affiliateAccount    affiliate account
      * @param _affiliateFee        affiliate fee in PPM
      * 
      * @return the amount of BNT received from this conversion
    */
    function xConvert(
        IERC20Token[] _path,
        uint256 _amount,
        uint256 _minReturn,
        bytes32 _toBlockchain,
        bytes32 _to,
        uint256 _conversionId,
        address _affiliateAccount,
        uint256 _affiliateFee
    )
        public
        payable
        returns (uint256)
    {
        IBancorX bancorX = IBancorX(addressOf(BANCOR_X));
        IBancorNetwork bancorNetwork = IBancorNetwork(addressOf(BANCOR_NETWORK));
        IERC20Token targetToken = _path[_path.length - 1];

        // verify that the destination token is BNT
        require(targetToken == addressOf(BNT_TOKEN));

        // we need to transfer the source tokens from the caller to the BancorNetwork contract,
        // so it can execute the conversion on behalf of the caller

        // not ETH, transfer the tokens directly to the BancorNetwork contract
        if (msg.value == 0)
            require(_path[0].transferFrom(msg.sender, bancorNetwork, _amount));

        // execute the conversion and pass on the ETH with the call
        uint256 result = bancorNetwork.convertFor2.value(msg.value)(_path, _amount, _minReturn, this, _affiliateAccount, _affiliateFee);

        // grant BancorX allowance
        ensureAllowance(targetToken, bancorX, result);

        // transfer the resulting amount to BancorX
        IBancorX(bancorX).xTransfer(_toBlockchain, _to, result, _conversionId);

        return result;
    }

    /**
      * @dev allows a user to convert BNT that was sent from another blockchain into any other
      * token on the Bancor Network without specifying the amount of BNT to be converted, but
      * rather by providing the xTransferId which allows us to get the amount from BancorX.
      * 
      * @param _path            conversion path, see conversion path format in the BancorNetwork contract
      * @param _minReturn       if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
      * @param _conversionId    pre-determined unique (if non zero) id which refers to this transaction 
      * 
      * @return tokens issued in return
    */
    function completeXConversion(IERC20Token[] _path, uint256 _minReturn, uint256 _conversionId) public returns (uint256) {
        IBancorX bancorX = IBancorX(addressOf(BANCOR_X));
        IBancorNetwork bancorNetwork = IBancorNetwork(addressOf(BANCOR_NETWORK));

        // verify that the source token is BNT
        require(_path[0] == addressOf(BNT_TOKEN));

        // get conversion amount from BancorX contract
        uint256 amount = bancorX.getXTransferAmount(_conversionId, msg.sender);

        // transfer the tokens directly to the BancorNetwork contract
        require(_path[0].transferFrom(msg.sender, bancorNetwork, amount));

        // execute the conversion and transfer the result back to the sender
        return bancorNetwork.convertFor2(_path, amount, _minReturn, msg.sender, address(0), 0);
    }

    /**
      * @dev utility, checks whether allowance for the given spender exists and approves one if it doesn't.
      * Note that we use the non standard erc-20 interface in which `approve` has no return value so that
      * this function will work for both standard and non standard tokens
      * 
      * @param _token   token to check the allowance in
      * @param _spender approved address
      * @param _value   allowance amount
    */
    function ensureAllowance(IERC20Token _token, address _spender, uint256 _value) private {
        uint256 allowance = _token.allowance(this, _spender);
        if (allowance < _value) {
            if (allowance > 0)
                require(_token.approve(_spender, 0));
            require(_token.approve(_spender, _value));
        }
    }
}
/**
 * @dev BancorX Helper
 * 
 */
function xConvert(
    IERC20Token[] _path,
    uint256 _amount,
    uint256 _minReturn,
    bytes32 _toBlockchain,
    bytes32 _to,
    uint256 _conversionId,
    address _affiliateAccount,
    uint256 _affiliateFee
)
    public
    payable
    returns (uint256)
{
    IBancorX bancorX = IBancorX(addressOf(BANCOR_X));
    IBancorNetwork bancorNetwork = IBancorNetwork(addressOf(BANCOR_NETWORK));
    IERC20Token targetToken = _path[_path.length - 1];

    // verify that the destination token is BNT
    require(targetToken == addressOf(BNT_TOKEN));

    // we need to transfer the source tokens from the caller to the BancorNetwork contract,
    // so it can execute the conversion on behalf of the caller

    // not ETH, transfer the tokens directly to the BancorNetwork contract
    if (msg.value == 0)
        require(_path[0].transferFrom(msg.sender, bancorNetwork, _amount));

    // execute the conversion and pass on the ETH with the call
    uint256 result = bancorNetwork.convertFor2.value(msg.value)(_path, _amount, _minReturn, this, _affiliateAccount, _affiliateFee);

    // grant BancorX allowance
    ensureAllowance(targetToken, bancorX, result);

    // transfer the resulting amount to BancorX
    IBancorX(bancorX).xTransfer(_toBlockchain, _to, result, _conversionId);

    return result;
}

/**
 * @dev converts any other token to BNT in the bancor network
 * by following a predefined conversion path and transfers the resulting
 * tokens to BancorX.
 * note that the contract should already have been given allowance for the source token (if not ETH)
 * 
 * @param _path                conversion path, see conversion path format above
 * @param _amount              amount to convert from (in the initial source token)
 * @param _minReturn           if the conversion results in an amount smaller than the minimum return - it is cancelled, must be nonzero
 * @param _toBlockchain        blockchain BNT will be issued on
 * @param _to                  address/account on _toBlockchain to send the BNT to
 * @param _conversionId        pre-determined unique (if non zero) id which refers to this transaction 
 * @param _affiliateAccount    affiliate account
 * @param _affiliateFee        affiliate fee in PPM
 * 
 * @return the amount of BNT received from this conversion
 */