library EtherDeltaTrader {

  address constant public ETHERDELTA_ADDR = 0x8d12a197cb00d4747a1fe03395095ce2a5cc6819; // EtherDelta contract address
  
  /// @dev Gets the address of EtherDelta contract
  /// @return Address of EtherDelta Contract.
  function getEtherDeltaAddresss() internal returns(address) {
      return ETHERDELTA_ADDR;
  }
   
  /// @dev Fills a sell order in EtherDelta.
  /// @param orderAddresses Array of order's maker, makerToken, takerToken.
  /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, expires and nonce.
  /// @param exchangeFee Fee percentage of the exchange.
  /// @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 exchangeFee,
    uint fillTakerTokenAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) {
    
    //Deposit ethers in EtherDelta
    deposit(fillTakerTokenAmount);
    
    uint amountToTrade;
    uint fee;
      
    //Substract EtherDelta fee
    (amountToTrade, fee) = substractFee(exchangeFee, fillTakerTokenAmount);
    
    //Fill EtherDelta Order
    trade(
      orderAddresses,
      orderValues, 
      amountToTrade,
      v, 
      r, 
      s
    );
    
    //Withdraw token from EtherDelta
    withdrawToken(orderAddresses[1], getPartialAmount(orderValues[0], orderValues[1], amountToTrade));
    
    //Returns the amount of token obtained
    return getPartialAmount(orderValues[0], orderValues[1], amountToTrade);
 
  }
  
  /// @dev Fills a buy order in EtherDelta.
  /// @param orderAddresses Array of order's maker, makerToken, takerToken.
  /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, expires and nonce.
  /// @param exchangeFee Fee percentage of the exchange.
  /// @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 ethers filled in trade
  function fillBuyOrder(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint exchangeFee,
    uint fillTakerTokenAmount,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) {
    
    //Deposit tokens in EtherDelta
    depositToken(orderAddresses[2], fillTakerTokenAmount);
    
    uint amountToTrade;
    uint fee;
      
    //Substract EtherDelta fee
    (amountToTrade, fee) = substractFee(exchangeFee, fillTakerTokenAmount);

    //Fill EtherDelta Order
    trade(
      orderAddresses,
      orderValues, 
      amountToTrade,
      v, 
      r, 
      s
    );
    
    //Withdraw ethers obtained from EtherDelta
    withdraw(getPartialAmount(orderValues[0], orderValues[1], amountToTrade));
    
    //Returns the amount of ethers obtained
    return getPartialAmount(orderValues[0], orderValues[1], amountToTrade);
  }
  
  /// @dev Trade an EtherDelta order.
  /// @param orderAddresses Array of order's maker, makerToken, takerToken.
  /// @param orderValues Array of order's makerTokenAmount, takerTokenAmount, expires and nonce.
  /// @param amountToTrade Desired amount of takerToken to fill.
  /// @param v ECDSA signature parameter v.
  /// @param r ECDSA signature parameters r.
  /// @param s ECDSA signature parameters s.
  function trade(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint amountToTrade,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal {
      
     //Fill EtherDelta Order
     EtherDelta(ETHERDELTA_ADDR).trade(
      orderAddresses[2], 
      orderValues[1], 
      orderAddresses[1], 
      orderValues[0], 
      orderValues[2], 
      orderValues[3], 
      orderAddresses[0], 
      v, 
      r, 
      s, 
      amountToTrade
    );
  }
  
  /// @dev Get the available amount of an EtherDelta order.
  /// @param orderAddresses Array of address arrays containing individual order addresses.
  /// @param orderValues Array of uint arrays containing individual order values.
  /// @param exchangeFee Fee percentage of the exchange.
  /// @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 the order.
  function getAvailableAmount(
    address[5] orderAddresses,
    uint[6] orderValues,
    uint exchangeFee,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) internal returns(uint) {
      
    //If expired return 0 
    if(block.number > orderValues[2])
      return 0;
      
    //Fill EtherDelta Order
    uint availableVolume = EtherDelta(ETHERDELTA_ADDR).availableVolume(
      orderAddresses[2], 
      orderValues[1], 
      orderAddresses[1], 
      orderValues[0], 
      orderValues[2], 
      orderValues[3], 
      orderAddresses[0], 
      v, 
      r, 
      s
    );
    
    //AvailableVolume adds the fee percentage (X - X * fee)
    return getPartialAmount(availableVolume, SafeMath.safeSub(1 ether, exchangeFee), 1 ether);
  }
  
  /// @dev Substracts the take fee from EtherDelta.
  /// @param amount Amount to substract EtherDelta fee
  /// @return Amount minus the EtherDelta fee.
  function substractFee(uint feePercentage, uint amount) constant internal returns(uint, uint) {
    uint fee = getPartialAmount(amount, 1 ether, feePercentage);
    return (SafeMath.safeSub(amount, fee), fee);
  }
  
  /// @dev Deposit ethers to EtherDelta.
  /// @param amount Amount of ethers to deposit in EtherDelta
  function deposit(uint amount) internal {
    EtherDelta(ETHERDELTA_ADDR).deposit.value(amount)();
  }
  
  /// @dev Deposit tokens to EtherDelta.
  /// @param token Address of token to deposit in EtherDelta
  /// @param amount Amount of tokens to deposit in EtherDelta
  function depositToken(address token, uint amount) internal {
    Token(token).approve(ETHERDELTA_ADDR, amount);
    EtherDelta(ETHERDELTA_ADDR).depositToken(token, amount);
  }
  
  /// @dev Withdraw ethers from EtherDelta
  /// @param amount Amount of ethers to withdraw from EtherDelta
  function withdraw(uint amount) internal { 
    EtherDelta(ETHERDELTA_ADDR).withdraw(amount);
  }
   
  /// @dev Withdraw tokens from EtherDelta.
  /// @param token Address of token to withdraw from EtherDelta
  /// @param amount Amount of tokens to withdraw from EtherDelta
  function withdrawToken(address token, uint amount) internal { 
    EtherDelta(ETHERDELTA_ADDR).withdrawToken(token, 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)
    public
    constant
    returns (uint)
  {
    return SafeMath.safeDiv(SafeMath.safeMul(numerator, target), denominator);
  }

}
function depositToken(address token, uint amount) internal {
  Token(token).approve(ETHERDELTA_ADDR, amount);
  EtherDelta(ETHERDELTA_ADDR).depositToken(token, amount);
}

/// @dev Deposit tokens to EtherDelta.
/// @param token Address of token to deposit in EtherDelta
/// @param amount Amount of tokens to deposit in EtherDelta