library ZrxTrader {
    
  uint16 constant public EXTERNAL_QUERY_GAS_LIMIT = 4999;    // Changes to state require at least 5000 gas

  address constant public ZRX_EXCHANGE_ADDR = 0x12459c951127e0c374ff9105dda097662a027093; // 0x Exchange contract address
  address constant public TOKEN_TRANSFER_PROXY_ADDR = 0x8da0d80f5007ef1e431dd2127178d224e32c2ef4; // TokenTransferProxy contract address
  address constant public WETH_ADDR = 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2; // Wrapped ether address (WETH)
  address constant public ZRX_TOKEN_ADDR = 0xe41d2489571d322189246dafa5ebde1f4699f498;
  
  /// @dev Gets the address of the Wrapped Ether contract
  /// @return Address of Weth Contract.
  function getWethAddress() internal returns(address) {
      return WETH_ADDR;
  }
  
  /// @dev Fills a sell order in 0x.
  /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
  /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
  /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
  /// @param v ECDSA signature parameter v.
  /// @param r ECDSA signature parameters r.
  /// @param s ECDSA signature parameters s.
  /// @return Total amount of takerToken filled in trade.
  function fillSellOrder(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint fillTakerTokenAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) 
  {
     
    // Wrap ETH to WETH
    depositWeth(fillTakerTokenAmount);
    
    // Allow 0x Transfer Token Proxy to transfer WETH
    aproveToken(WETH_ADDR, fillTakerTokenAmount);
    
    //Allow ZRX Transfer Token Proxy to transfer the token for fees
    aproveToken(ZRX_TOKEN_ADDR, getPartialAmount(fillTakerTokenAmount, orderValues[1], orderValues[3]));
    
    uint ethersSpent = Exchange(ZRX_EXCHANGE_ADDR).fillOrder(
      orderAddresses,
      orderValues,
      fillTakerTokenAmount,
      true,
      v,
      r,
      s
    );
    
    // Fill 0x order
    return getPartialAmount(orderValues[0], orderValues[1], ethersSpent);
    
  }
  
  /// @dev Fills a buy order in 0x.
  /// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
  /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
  /// @param fillTakerTokenAmount Desired amount of takerToken to fill.
  /// @param v ECDSA signature parameter v.
  /// @param r ECDSA signature parameters r.
  /// @param s ECDSA signature parameters s.
  /// @return Total amount of takerToken filled in trade.
  function fillBuyOrder(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint fillTakerTokenAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) 
  {
    
    //Allow 0x Transfer Token Proxy to transfer the token
    aproveToken(orderAddresses[3], fillTakerTokenAmount);
    
    //Allow ZRX Transfer Token Proxy to transfer the token for fees
    aproveToken(ZRX_TOKEN_ADDR, getPartialAmount(fillTakerTokenAmount, orderValues[1], orderValues[3]));
    
    // Fill 0x order
    uint tokensSold = Exchange(ZRX_EXCHANGE_ADDR).fillOrder(
      orderAddresses,
      orderValues,
      fillTakerTokenAmount,
      true,
      v,
      r,
      s
    );
    
    uint ethersObtained = getPartialAmount(orderValues[0], orderValues[1], tokensSold);
    
    // Unwrap WETH to ETH
    withdrawWeth(ethersObtained);
    
    return ethersObtained;
  }
  
  /// @dev Get the available amount of a 0x order.
  /// @param orderAddresses Array of address arrays containing individual order addresses.
  /// @param orderValues Array of uint arrays containing individual order values.
  /// @param v Array ECDSA signature v parameters.
  /// @param r Array of ECDSA signature r parameters.
  /// @param s Array of ECDSA signature s parameters.
  /// @return Available amount of a 0x order.
  function getAvailableAmount(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) {
      
      //If expired return 0 
      if(block.timestamp >= orderValues[4])
        return 0;
          
      bytes32 orderHash = Exchange(ZRX_EXCHANGE_ADDR).getOrderHash(orderAddresses, orderValues);
      
      uint unAvailable = Exchange(ZRX_EXCHANGE_ADDR).getUnavailableTakerTokenAmount(orderHash);
  
      //Gets the available amount acoording to 0x contract substracting takerTokenAmount with unavailable takerTokenAmount
      uint availableByContract = SafeMath.safeSub(orderValues[1], unAvailable);
      
      //Gets the available amount according of the maker token balance
      uint availableByBalance =  getPartialAmount(getBalance(orderAddresses[2], orderAddresses[0]), orderValues[0], orderValues[1]);
      
      //Gets the available amount according how much allowance of the token the maker has
      uint availableByAllowance = getPartialAmount(getAllowance(orderAddresses[2], orderAddresses[0]), orderValues[0], orderValues[1]);
    
      //Get the available amount according to how much ZRX fee the maker can pay 
      uint zrxAmount = getAllowance(ZRX_TOKEN_ADDR, orderAddresses[0]);
      uint availableByZRX = getPartialAmount(zrxAmount, orderValues[2], orderValues[1]);
      
      //Returns the min value of all available results
      return SafeMath.min256(SafeMath.min256(SafeMath.min256(availableByContract, availableByBalance), availableByAllowance), availableByZRX);
  }
    
  /// @dev Deposit WETH
  /// @param amount Amount of ether to wrap.
  function depositWeth(uint amount) internal {
    EtherToken(WETH_ADDR).deposit.value(amount)();
  }
  
  /// @dev Approve tokens to be transfered by 0x Token Transfer Proxy
  /// @param token Address of token to approve.
  /// @param amount Amount of tokens to approve.
  function aproveToken(address token, uint amount) internal {
    Token(token).approve(TOKEN_TRANSFER_PROXY_ADDR, amount);
  }
  
  /// @dev Withdraw WETH
  /// @param amount Amount of ether to unwrap.
  function withdrawWeth(uint amount) internal { 
    EtherToken(WETH_ADDR).withdraw(amount);
  }
  
  /// @dev Calculates partial value given a numerator and denominator.
  /// @param numerator Numerator.
  /// @param denominator Denominator.
  /// @param target Value to calculate partial of.
  /// @return Partial value of target.
  function getPartialAmount(uint numerator, uint denominator, uint target) internal constant returns (uint)
  {
    return SafeMath.safeDiv(SafeMath.safeMul(numerator, target), denominator);
  }

  /// @dev Get token balance of an address.
  /// @param token Address of token.
  /// @param owner Address of owner.
  /// @return Token balance of owner.
  function getBalance(address token, address owner) internal constant returns (uint)
  {
    return Token(token).balanceOf.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner); // Limit gas to prevent reentrancy
  }

  /// @dev Get allowance of token given to TokenTransferProxy by an address.
  /// @param token Address of token.
  /// @param owner Address of owner.
  /// @return Allowance of token given to TokenTransferProxy by owner.
  function getAllowance(address token, address owner) internal constant returns (uint)
  {
    return Token(token).allowance.gas(EXTERNAL_QUERY_GAS_LIMIT)(owner, TOKEN_TRANSFER_PROXY_ADDR); // Limit gas to prevent reentrancy
  }
  
 
}
function fillBuyOrder(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint fillTakerTokenAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) 
  {
    
    //Allow 0x Transfer Token Proxy to transfer the token
    aproveToken(orderAddresses[3], fillTakerTokenAmount);
    
    //Allow ZRX Transfer Token Proxy to transfer the token for fees
    aproveToken(ZRX_TOKEN_ADDR, getPartialAmount(fillTakerTokenAmount, orderValues[1], orderValues[3]));
    
    // Fill 0x order
    uint tokensSold = Exchange(ZRX_EXCHANGE_ADDR).fillOrder(
      orderAddresses,
      orderValues,
      fillTakerTokenAmount,
      true,
      v,
      r,
      s
    );
    
    uint ethersObtained = getPartialAmount(orderValues[0], orderValues[1], tokensSold);
    
    // Unwrap WETH to ETH
    withdrawWeth(ethersObtained);
    
    return ethersObtained;
  }
/// @dev Fills a buy order in 0x.
/// @param orderAddresses Array of order's maker, taker, makerToken, takerToken, and feeRecipient.
/// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, makerFee, takerFee, expirationTimestampInSec, and salt.
/// @param fillTakerTokenAmount Desired amount of takerToken to fill.
/// @param v ECDSA signature parameter v.
/// @param r ECDSA signature parameters r.
/// @param s ECDSA signature parameters s.
/// @return Total amount of takerToken filled in trade.