pragma solidity ^0.4.15;

/**
 * @title Log Various Error Types
 * @author Adam Lemmon <adam@oraclize.it>
 * @dev Inherit this contract and your may now log errors easily
 * To support various error types, params, etc.
 */
contract LoggingErrors {
  /**
  * Events
  */
  event LogErrorString(string errorString);

  /**
  * Error cases
  */

  /**
   * @dev Default error to simply log the error message and return
   * @param _errorMessage The error message to log
   * @return ALWAYS false
   */
  function error(string _errorMessage) internal returns(bool) {
    LogErrorString(_errorMessage);
    return false;
  }
}

/**
 * @title Wallet Connector
 * @dev Connect the wallet contract to the correct Wallet Logic version
 */
contract WalletConnector is LoggingErrors {
  /**
   * Storage
   */
  address public owner_;
  address public latestLogic_;
  uint256 public latestVersion_;
  mapping(uint256 => address) public logicVersions_;
  uint256 public birthBlock_;

  /**
   * Events
   */
  event LogLogicVersionAdded(uint256 version);
  event LogLogicVersionRemoved(uint256 version);

  /**
   * @dev Constructor to set the latest logic address
   * @param _latestVersion Latest version of the wallet logic
   * @param _latestLogic Latest address of the wallet logic contract
   */
  function WalletConnector (
    uint256 _latestVersion,
    address _latestLogic
  ) public {
    owner_ = msg.sender;
    latestLogic_ = _latestLogic;
    latestVersion_ = _latestVersion;
    logicVersions_[_latestVersion] = _latestLogic;
    birthBlock_ = block.number;
  }

  /**
   * Add a new version of the logic contract
   * @param _version The version to be associated with the new contract.
   * @param _logic New logic contract.
   * @return Success of the transaction.
   */
  function addLogicVersion (
    uint256 _version,
    address _logic
  ) external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner, WalletConnector.addLogicVersion()');

    if (logicVersions_[_version] != 0)
      return error('Version already exists, WalletConnector.addLogicVersion()');

    // Update latest if this is the latest version
    if (_version > latestVersion_) {
      latestLogic_ = _logic;
      latestVersion_ = _version;
    }

    logicVersions_[_version] = _logic;
    LogLogicVersionAdded(_version);

    return true;
  }

  /**
   * @dev Remove a version. Cannot remove the latest version.
   * @param  _version The version to remove.
   */
  function removeLogicVersion(uint256 _version) external {
    require(msg.sender == owner_);
    require(_version != latestVersion_);
    delete logicVersions_[_version];
    LogLogicVersionRemoved(_version);
  }

  /**
   * Constants
   */

  /**
   * Called from user wallets in order to upgrade their logic.
   * @param _version The version to upgrade to. NOTE pass in 0 to upgrade to latest.
   * @return The address of the logic contract to upgrade to.
   */
  function getLogic(uint256 _version)
    external
    constant
    returns(address)
  {
    if (_version == 0)
      return latestLogic_;
    else
      return logicVersions_[_version];
  }
}

/**
 * @title Wallet to hold and trade ERC20 tokens and ether
 * @author Adam Lemmon <adam@oraclize.it>
 * @dev User wallet to interact with the exchange.
 * all tokens and ether held in this wallet, 1 to 1 mapping to user EOAs.
 */
contract Wallet is LoggingErrors {
  /**
   * Storage
   */
  // Vars included in wallet logic "lib", the order must match between Wallet and Logic
  address public owner_;
  address public exchange_;
  mapping(address => uint256) public tokenBalances_;

  address public logic_; // storage location 0x3 loaded for delegatecalls so this var must remain at index 3
  uint256 public birthBlock_;

  // Address updated at deploy time
  WalletConnector private connector_ = WalletConnector(0x03d6e7b2f48120fd57a89ff0bbd56e9ec39af21c);

  /**
   * Events
   */
  event LogDeposit(address token, uint256 amount, uint256 balance);
  event LogWithdrawal(address token, uint256 amount, uint256 balance);

  /**
   * @dev Contract consturtor. Set user as owner and connector address.
   * @param _owner The address of the user's EOA, wallets created from the exchange
   * so must past in the owner address, msg.sender == exchange.
   */
  function Wallet(address _owner) public {
    owner_ = _owner;
    exchange_ = msg.sender;
    logic_ = connector_.latestLogic_();
    birthBlock_ = block.number;
  }

  /**
   * @dev Fallback - Only enable funds to be sent from the exchange.
   * Ensures balances will be consistent.
   */
  function () external payable {
    require(msg.sender == exchange_);
  }

  /**
  * External
  */

  /**
   * @dev Deposit ether into this wallet, default to address 0 for consistent token lookup.
   */
  function depositEther()
    external
    payable
  {
    require(logic_.delegatecall(bytes4(sha3('deposit(address,uint256)')), 0, msg.value));
  }

  /**
   * @dev Deposit any ERC20 token into this wallet.
   * @param _token The address of the existing token contract.
   * @param _amount The amount of tokens to deposit.
   * @return Bool if the deposit was successful.
   */
  function depositERC20Token (
    address _token,
    uint256 _amount
  ) external
    returns(bool)
  {
    // ether
    if (_token == 0)
      return error('Cannot deposit ether via depositERC20, Wallet.depositERC20Token()');

    require(logic_.delegatecall(bytes4(sha3('deposit(address,uint256)')), _token, _amount));
    return true;
  }

  /**
   * @dev The result of an order, update the balance of this wallet.
   * @param _token The address of the token balance to update.
   * @param _amount The amount to update the balance by.
   * @param _subtractionFlag If true then subtract the token amount else add.
   * @return Bool if the update was successful.
   */
  function updateBalance (
    address _token,
    uint256 _amount,
    bool _subtractionFlag
  ) external
    returns(bool)
  {
    assembly {
      calldatacopy(0x40, 0, calldatasize)
      delegatecall(gas, sload(0x3), 0x40, calldatasize, 0, 32)
      return(0, 32)
      pop
    }
  }

  /**
   * User may update to the latest version of the exchange contract.
   * Note that multiple versions are NOT supported at this time and therefore if a
   * user does not wish to update they will no longer be able to use the exchange.
   * @param _exchange The new exchange.
   * @return Success of this transaction.
   */
  function updateExchange(address _exchange)
    external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner_, Wallet.updateExchange()');

    // If subsequent messages are not sent from this address all orders will fail
    exchange_ = _exchange;

    return true;
  }

  /**
   * User may update to a new or older version of the logic contract.
   * @param _version The versin to update to.
   * @return Success of this transaction.
   */
  function updateLogic(uint256 _version)
    external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner_, Wallet.updateLogic()');

    address newVersion = connector_.getLogic(_version);

    // Invalid version as defined by connector
    if (newVersion == 0)
      return error('Invalid version, Wallet.updateLogic()');

    logic_ = newVersion;
    return true;
  }

  /**
   * @dev Verify an order that the Exchange has received involving this wallet.
   * Internal checks and then authorize the exchange to move the tokens.
   * If sending ether will transfer to the exchange to broker the trade.
   * @param _token The address of the token contract being sold.
   * @param _amount The amount of tokens the order is for.
   * @param _fee The fee for the current trade.
   * @param _feeToken The token of which the fee is to be paid in.
   * @return If the order was verified or not.
   */
  function verifyOrder (
    address _token,
    uint256 _amount,
    uint256 _fee,
    address _feeToken
  ) external
    returns(bool)
  {
    assembly {
      calldatacopy(0x40, 0, calldatasize)
      delegatecall(gas, sload(0x3), 0x40, calldatasize, 0, 32)
      return(0, 32)
      pop
    }
  }

  /**
   * @dev Withdraw any token, including ether from this wallet to an EOA.
   * @param _token The address of the token to withdraw.
   * @param _amount The amount to withdraw.
   * @return Success of the withdrawal.
   */
  function withdraw(address _token, uint256 _amount)
    external
    returns(bool)
  {
    if(msg.sender != owner_)
      return error('msg.sender != owner, Wallet.withdraw()');

    assembly {
      calldatacopy(0x40, 0, calldatasize)
      delegatecall(gas, sload(0x3), 0x40, calldatasize, 0, 32)
      return(0, 32)
      pop
    }
  }

  /**
   * Constants
   */

  /**
   * @dev Get the balance for a specific token.
   * @param _token The address of the token contract to retrieve the balance of.
   * @return The current balance within this contract.
   */
  function balanceOf(address _token)
    public
    constant
    returns(uint)
  {
    return tokenBalances_[_token];
  }
}

/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
  function mul(uint256 a, uint256 b) internal constant returns (uint256) {
    uint256 c = a * b;
    assert(a == 0 || c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal constant returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal constant returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal constant returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}


contract Token {
  /// @return total amount of tokens
  function totalSupply() constant returns (uint256 supply) {}

  /// @param _owner The address from which the balance will be retrieved
  /// @return The balance
  function balanceOf(address _owner) constant returns (uint256 balance) {}

  /// @notice send `_value` token to `_to` from `msg.sender`
  /// @param _to The address of the recipient
  /// @param _value The amount of token to be transferred
  /// @return Whether the transfer was successful or not
  function transfer(address _to, uint256 _value) returns (bool success) {}

  /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
  /// @param _from The address of the sender
  /// @param _to The address of the recipient
  /// @param _value The amount of token to be transferred
  /// @return Whether the transfer was successful or not
  function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}

  /// @notice `msg.sender` approves `_addr` to spend `_value` tokens
  /// @param _spender The address of the account able to transfer the tokens
  /// @param _value The amount of wei to be approved for transfer
  /// @return Whether the approval was successful or not
  function approve(address _spender, uint256 _value) returns (bool success) {}

  /// @param _owner The address of the account owning tokens
  /// @param _spender The address of the account able to transfer the tokens
  /// @return Amount of remaining tokens allowed to spent
  function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}

  event Transfer(address indexed _from, address indexed _to, uint256 _value);
  event Approval(address indexed _owner, address indexed _spender, uint256 _value);

  uint public decimals;
  string public name;
}


/**
 * @title Decentralized exchange for ether and ERC20 tokens.
 * @author Adam Lemmon <adam@oraclize.it>
 * @dev All trades brokered by this contract.
 * Orders submitted by off chain order book and this contract handles
 * verification and execution of orders.
 * All value between parties is transferred via this exchange.
 * Methods arranged by visibility; external, public, internal, private and alphabatized within.
 */
contract Exchange is LoggingErrors {

  using SafeMath for uint256;

  /**
   * Data Structures
   */
  struct Order {
    bool active_;  // True: active, False: filled or cancelled
    address offerToken_;
    uint256 offerTokenTotal_;
    uint256 offerTokenRemaining_;  // Amount left to give
    address wantToken_;
    uint256 wantTokenTotal_;
    uint256 wantTokenReceived_;  // Amount received, note this may exceed want total
  }

  /**
   * Storage
   */
  address private orderBookAccount_;
  address private owner_;
  uint256 public minOrderEthAmount_;
  uint256 public birthBlock_;
  address public edoToken_;
  uint256 public edoPerWei_;
  uint256 public edoPerWeiDecimals_;
  address public eidooWallet_;
  mapping(bytes32 => Order) public orders_; // Map order hashes to order data struct
  mapping(address => address) public userAccountToWallet_; // User EOA to wallet addresses

  /**
   * Events
   */
  event LogEdoRateSet(uint256 rate);
  event LogOrderExecutionSuccess();
  event LogOrderFilled(bytes32 indexed orderId, uint256 fillAmount, uint256 fillRemaining);
  event LogUserAdded(address indexed user, address walletAddress);
  event LogWalletDeposit(address indexed walletAddress, address token, uint256 amount, uint256 balance);
  event LogWalletWithdrawal(address indexed walletAddress, address token, uint256 amount, uint256 balance);

  /**
   * @dev Contract constructor - CONFIRM matches contract name.  Set owner and addr of order book.
   * @param _bookAccount The EOA address for the order book, will submit ALL orders.
   * @param _minOrderEthAmount Minimum amount of ether that each order must contain.
   * @param _edoToken Deployed edo token.
   * @param _edoPerWei Rate of edo tokens per wei.
   * @param _edoPerWeiDecimals Decimlas carried in edo rate.
   * @param _eidooWallet Wallet to pay fees to.
   */
  function Exchange(
    address _bookAccount,
    uint256 _minOrderEthAmount,
    address _edoToken,
    uint256 _edoPerWei,
    uint256 _edoPerWeiDecimals,
    address _eidooWallet
  ) public {
    orderBookAccount_ = _bookAccount;
    minOrderEthAmount_ = _minOrderEthAmount;
    owner_ = msg.sender;
    birthBlock_ = block.number;
    edoToken_ = _edoToken;
    edoPerWei_ = _edoPerWei;
    edoPerWeiDecimals_ = _edoPerWeiDecimals;
    eidooWallet_ = _eidooWallet;
  }

  /**
   * @dev Fallback. wallets utilize to send ether in order to broker trade.
   */
  function () external payable { }

  /**
   * External
   */

  /**
   * @dev Add a new user to the exchange, create a wallet for them.
   * Map their account address to the wallet contract for lookup.
   * @param _userAccount The address of the user's EOA.
   * @return Success of the transaction, false if error condition met.
   */
  function addNewUser(address _userAccount)
    external
    returns (bool)
  {
    if (userAccountToWallet_[_userAccount] != address(0))
      return error('User already exists, Exchange.addNewUser()');

    // Pass the userAccount address to wallet constructor so owner is not the exchange contract
    address userWallet = new Wallet(_userAccount);

    userAccountToWallet_[_userAccount] = userWallet;

    LogUserAdded(_userAccount, userWallet);

    return true;
  }

  /**
   * Execute orders in batches.
   * @param  _token_and_EOA_Addresses Tokan and user addresses.
   * @param  _amountsExpirationAndSalt Offer and want token amount and expiration and salt values.
   * @param _sig_v All order signature v values.
   * @param _sig_r_and_s All order signature r and r values.
   * @return The success of this transaction.
   */
  function batchExecuteOrder(
    address[4][] _token_and_EOA_Addresses,
    uint256[8][] _amountsExpirationAndSalt, // Packing to save stack size
    uint8[2][] _sig_v,
    bytes32[4][] _sig_r_and_s
  ) external
    returns(bool)
  {
    for (uint256 i = 0; i < _amountsExpirationAndSalt.length; i++) {
      require(executeOrder(
        _token_and_EOA_Addresses[i],
        _amountsExpirationAndSalt[i],
        _sig_v[i],
        _sig_r_and_s[i]
      ));
    }

    return true;
  }

  /**
   * @dev Execute an order that was submitted by the external order book server.
   * The order book server believes it to be a match.
   * There are components for both orders, maker and taker, 2 signatures as well.
   * @param _token_and_EOA_Addresses The addresses of the maker and taker EOAs and offered token contracts.
   * [makerEOA, makerOfferToken, takerEOA, takerOfferToken]
   * @param _amountsExpirationAndSalt The amount of tokens, [makerOffer, makerWant, takerOffer, takerWant].
   * and the block number at which this order expires
   * and a random number to mitigate replay. [makerExpiry, makerSalt, takerExpiry, takerSalt]
   * @param _sig_v ECDSA signature parameter v, maker 0 and taker 1.
   * @param _sig_r_and_s ECDSA signature parameters r ans s, maker 0, 1 and taker 2, 3.
   * @return Success of the transaction, false if error condition met.
   * Like types grouped to eliminate stack depth error
   */
  function executeOrder (
    address[4] _token_and_EOA_Addresses,
    uint256[8] _amountsExpirationAndSalt, // Packing to save stack size
    uint8[2] _sig_v,
    bytes32[4] _sig_r_and_s
  ) public
    returns(bool)
  {
    // Only read wallet addresses from storage once
    // Need one more stack slot so squashing into array
    Wallet[2] memory wallets = [
      Wallet(userAccountToWallet_[_token_and_EOA_Addresses[0]]), // maker
      Wallet(userAccountToWallet_[_token_and_EOA_Addresses[2]]) // taker
    ];

    // Basic pre-conditions, return if any input data is invalid
    if(!__executeOrderInputIsValid__(
      _token_and_EOA_Addresses,
      _amountsExpirationAndSalt,
      wallets[0],
      wallets[1]
    ))
      return error('Input is invalid, Exchange.executeOrder()');

    // Verify Maker and Taker signatures
    bytes32 makerOrderHash;
    bytes32 takerOrderHash;
    (makerOrderHash, takerOrderHash) = __generateOrderHashes__(_token_and_EOA_Addresses, _amountsExpirationAndSalt);

    if (!__signatureIsValid__(
      _token_and_EOA_Addresses[0],
      makerOrderHash,
      _sig_v[0],
      _sig_r_and_s[0],
      _sig_r_and_s[1]
    ))
      return error('Maker signature is invalid, Exchange.executeOrder()');

    if (!__signatureIsValid__(
      _token_and_EOA_Addresses[2],
      takerOrderHash,
      _sig_v[1],
      _sig_r_and_s[2],
      _sig_r_and_s[3]
    ))
      return error('Taker signature is invalid, Exchange.executeOrder()');

    // Exchange Order Verification and matching.
    Order memory makerOrder = orders_[makerOrderHash];
    Order memory takerOrder = orders_[takerOrderHash];

    if (makerOrder.wantTokenTotal_ == 0) {  // Check for existence
      makerOrder.active_ = true;
      makerOrder.offerToken_ = _token_and_EOA_Addresses[1];
      makerOrder.offerTokenTotal_ = _amountsExpirationAndSalt[0];
      makerOrder.offerTokenRemaining_ = _amountsExpirationAndSalt[0]; // Amount to give
      makerOrder.wantToken_ = _token_and_EOA_Addresses[3];
      makerOrder.wantTokenTotal_ = _amountsExpirationAndSalt[1];
      makerOrder.wantTokenReceived_ = 0; // Amount received
    }

    if (takerOrder.wantTokenTotal_ == 0) {  // Check for existence
      takerOrder.active_ = true;
      takerOrder.offerToken_ = _token_and_EOA_Addresses[3];
      takerOrder.offerTokenTotal_ = _amountsExpirationAndSalt[2];
      takerOrder.offerTokenRemaining_ = _amountsExpirationAndSalt[2];  // Amount to give
      takerOrder.wantToken_ = _token_and_EOA_Addresses[1];
      takerOrder.wantTokenTotal_ = _amountsExpirationAndSalt[3];
      takerOrder.wantTokenReceived_ = 0; // Amount received
    }

    if (!__ordersMatch_and_AreVaild__(makerOrder, takerOrder))
      return error('Orders do not match, Exchange.executeOrder()');

    // Trade amounts
    uint256 toTakerAmount;
    uint256 toMakerAmount;
    (toTakerAmount, toMakerAmount) = __getTradeAmounts__(makerOrder, takerOrder);

    // TODO consider removing. Can this condition be met?
    if (toTakerAmount < 1 || toMakerAmount < 1)
      return error('Token amount < 1, price ratio is invalid! Token value < 1, Exchange.executeOrder()');

    // Taker is offering edo tokens so ensure sufficient balance in order to offer edo and pay fee in edo
    if (
        takerOrder.offerToken_ == edoToken_ &&
        Token(edoToken_).balanceOf(wallets[1]) < __calculateFee__(makerOrder, toTakerAmount, toMakerAmount).add(toMakerAmount)
      ) {
        return error('Taker has an insufficient EDO token balance to cover the fee AND the offer, Exchange.executeOrder()');
    // Taker has sufficent EDO token balance to pay the fee
    } else if (Token(edoToken_).balanceOf(wallets[1]) < __calculateFee__(makerOrder, toTakerAmount, toMakerAmount))
      return error('Taker has an insufficient EDO token balance to cover the fee, Exchange.executeOrder()');

    // Wallet Order Verification, reach out to the maker and taker wallets.
    if (!__ordersVerifiedByWallets__(
        _token_and_EOA_Addresses,
        toMakerAmount,
        toTakerAmount,
        wallets[0],
        wallets[1],
        __calculateFee__(makerOrder, toTakerAmount, toMakerAmount)
      ))
      return error('Order could not be verified by wallets, Exchange.executeOrder()');

    // Order Execution, Order Fully Verified by this point, time to execute!
    // Local order structs
    __updateOrders__(makerOrder, takerOrder, toTakerAmount, toMakerAmount);

    // Write to storage then external calls
    //  Update orders active flag if filled
    if (makerOrder.offerTokenRemaining_ == 0)
      makerOrder.active_ = false;

    if (takerOrder.offerTokenRemaining_ == 0)
      takerOrder.active_ = false;

    // Finally write orders to storage
    orders_[makerOrderHash] = makerOrder;
    orders_[takerOrderHash] = takerOrder;

    // Transfer the external value, ether <> tokens
    require(
      __executeTokenTransfer__(
        _token_and_EOA_Addresses,
        toTakerAmount,
        toMakerAmount,
        __calculateFee__(makerOrder, toTakerAmount, toMakerAmount),
        wallets[0],
        wallets[1]
      )
    );

    // Log the order id(hash), amount of offer given, amount of offer remaining
    LogOrderFilled(makerOrderHash, toTakerAmount, makerOrder.offerTokenRemaining_);
    LogOrderFilled(takerOrderHash, toMakerAmount, takerOrder.offerTokenRemaining_);

    LogOrderExecutionSuccess();

    return true;
  }

  /**
   * @dev Set the rate of wei per edo token in or to calculate edo fee
   * @param _edoPerWei Rate of edo tokens per wei.
   * @return Success of the transaction.
   */
  function setEdoRate(
    uint256 _edoPerWei
  ) external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner, Exchange.setEdoRate()');

    edoPerWei_ = _edoPerWei;

    LogEdoRateSet(edoPerWei_);

    return true;
  }

  /**
   * @dev Set the wallet for fees to be paid to.
   * @param _eidooWallet Wallet to pay fees to.
   * @return Success of the transaction.
   */
  function setEidooWallet(
    address _eidooWallet
  ) external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner, Exchange.setEidooWallet()');

    eidooWallet_ = _eidooWallet;

    return true;
  }

  /**
   * @dev Set the minimum amount of ether required per order.
   * @param _minOrderEthAmount Min amount of ether required per order.
   * @return Success of the transaction.
   */
  function setMinOrderEthAmount (
    uint256 _minOrderEthAmount
  ) external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner, Exchange.setMinOrderEtherAmount()');

    minOrderEthAmount_ = _minOrderEthAmount;

    return true;
  }

  /**
   * @dev Set a new order book account.
   * @param _account The new order book account.
   */
  function setOrderBookAcount (
    address _account
  ) external
    returns(bool)
  {
    if (msg.sender != owner_)
      return error('msg.sender != owner, Exchange.setOrderBookAcount()');

    orderBookAccount_ = _account;
    return true;
  }

  /*
   Methods to catch events from external contracts, user wallets primarily
   */

  /**
   * @dev Simply log the event to track wallet interaction off-chain
   * @param _token The address of the token that was deposited.
   * @param _amount The amount of the token that was deposited.
   * @param _walletBalance The updated balance of the wallet after deposit.
   */
  function walletDeposit(
    address _token,
    uint256 _amount,
    uint256 _walletBalance
  ) external
  {
    LogWalletDeposit(msg.sender, _token, _amount, _walletBalance);
  }

  /**
   * @dev Simply log the event to track wallet interaction off-chain
   * @param _token The address of the token that was deposited.
   * @param _amount The amount of the token that was deposited.
   * @param _walletBalance The updated balance of the wallet after deposit.
   */
  function walletWithdrawal(
    address _token,
    uint256 _amount,
    uint256 _walletBalance
  ) external
  {
    LogWalletWithdrawal(msg.sender, _token, _amount, _walletBalance);
  }

  /**
   * Private
   */

  /**
   * Calculate the fee for the given trade. Calculated as the set % of the wei amount
   * converted into EDO tokens using the manually set conversion ratio.
   * @param _makerOrder The maker order object.
   * @param _toTaker The amount of tokens going to the taker.
   * @param _toMaker The amount of tokens going to the maker.
   * @return The total fee to be paid in EDO tokens.
   */
  function __calculateFee__(
    Order _makerOrder,
    uint256 _toTaker,
    uint256 _toMaker
  ) private
    constant
    returns(uint256)
  {
    // weiAmount * (fee %) * (EDO/Wei) / (decimals in edo/wei) / (decimals in percentage)
    if (_makerOrder.offerToken_ == address(0)) {
      return _toTaker.mul(edoPerWei_).div(10**edoPerWeiDecimals_);
    } else {
      return _toMaker.mul(edoPerWei_).div(10**edoPerWeiDecimals_);
    }
  }

  /**
   * @dev Verify the input to order execution is valid.
   * @param _token_and_EOA_Addresses The addresses of the maker and taker EOAs and offered token contracts.
   * [makerEOA, makerOfferToken, takerEOA, takerOfferToken]
   * @param _amountsExpirationAndSalt The amount of tokens, [makerOffer, makerWant, takerOffer, takerWant].
   * as well as The block number at which this order expires, maker[4] and taker[6].
   * @return Success if all checks pass.
   */
  function __executeOrderInputIsValid__(
    address[4] _token_and_EOA_Addresses,
    uint256[8] _amountsExpirationAndSalt,
    address _makerWallet,
    address _takerWallet
  ) private
    constant
    returns(bool)
  {
    if (msg.sender != orderBookAccount_)
      return error('msg.sender != orderBookAccount, Exchange.__executeOrderInputIsValid__()');

    if (block.number > _amountsExpirationAndSalt[4])
      return error('Maker order has expired, Exchange.__executeOrderInputIsValid__()');

    if (block.number > _amountsExpirationAndSalt[6])
      return error('Taker order has expired, Exchange.__executeOrderInputIsValid__()');

    // Wallets
    if (_makerWallet == address(0))
      return error('Maker wallet does not exist, Exchange.__executeOrderInputIsValid__()');

    if (_takerWallet == address(0))
      return error('Taker wallet does not exist, Exchange.__executeOrderInputIsValid__()');

    // Tokens, addresses and amounts, ether exists
    if (_token_and_EOA_Addresses[1] != address(0) && _token_and_EOA_Addresses[3] != address(0))
      return error('Ether omitted! Is not offered by either the Taker or Maker, Exchange.__executeOrderInputIsValid__()');

    if (_token_and_EOA_Addresses[1] == address(0) && _token_and_EOA_Addresses[3] == address(0))
      return error('Taker and Maker offer token are both ether, Exchange.__executeOrderInputIsValid__()');

    if (
        _amountsExpirationAndSalt[0] == 0 ||
        _amountsExpirationAndSalt[1] == 0 ||
        _amountsExpirationAndSalt[2] == 0 ||
        _amountsExpirationAndSalt[3] == 0
      )
      return error('May not execute an order where token amount == 0, Exchange.__executeOrderInputIsValid__()');

    // Confirm order ether amount >= min amount
    // Maker
    uint256 minOrderEthAmount = minOrderEthAmount_; // Single storage read
    if (_token_and_EOA_Addresses[1] == 0 && _amountsExpirationAndSalt[0] < minOrderEthAmount)
      return error('Maker order does not meet the minOrderEthAmount_ of ether, Exchange.__executeOrderInputIsValid__()');

    // Taker
    if (_token_and_EOA_Addresses[3] == 0 && _amountsExpirationAndSalt[2] < minOrderEthAmount)
      return error('Taker order does not meet the minOrderEthAmount_ of ether, Exchange.__executeOrderInputIsValid__()');

    return true;
  }

  /**
   * @dev Execute the external transfer of tokens.
   * @param _token_and_EOA_Addresses The addresses of the maker and taker EOAs and offered token contracts.
   * [makerEOA, makerOfferToken, takerEOA, takerOfferToken]
   * @param _toTakerAmount The amount of tokens to transfer to the taker.
   * @param _toMakerAmount The amount of tokens to transfer to the maker.
   * @return Success if both wallets verify the order.
   */
  function __executeTokenTransfer__(
    address[4] _token_and_EOA_Addresses,
    uint256 _toTakerAmount,
    uint256 _toMakerAmount,
    uint256 _fee,
    Wallet _makerWallet,
    Wallet _takerWallet
  ) private
    returns (bool)
  {
    // Wallet mapping balances
    address makerOfferToken = _token_and_EOA_Addresses[1];
    address takerOfferToken = _token_and_EOA_Addresses[3];

    // Taker to pay fee before trading
    require(_takerWallet.updateBalance(edoToken_, _fee, true));  // Subtraction flag
    require(Token(edoToken_).transferFrom(_takerWallet, eidooWallet_, _fee));

    // Move the toTakerAmount from the maker to the taker
    require(_makerWallet.updateBalance(makerOfferToken, _toTakerAmount, true));  // Subtraction flag
      /*return error('Unable to subtract maker token from maker wallet, Exchange.__executeTokenTransfer__()');*/

    require(_takerWallet.updateBalance(makerOfferToken, _toTakerAmount, false));
      /*return error('Unable to add maker token to taker wallet, Exchange.__executeTokenTransfer__()');*/

    // Move the toMakerAmount from the taker to the maker
    require(_takerWallet.updateBalance(takerOfferToken, _toMakerAmount, true));  // Subtraction flag
      /*return error('Unable to subtract taker token from taker wallet, Exchange.__executeTokenTransfer__()');*/

    require(_makerWallet.updateBalance(takerOfferToken, _toMakerAmount, false));
      /*return error('Unable to add taker token to maker wallet, Exchange.__executeTokenTransfer__()');*/

    // Contract ether balances and token contract balances
    // Ether to the taker and tokens to the maker
    if (makerOfferToken == address(0)) {
      _takerWallet.transfer(_toTakerAmount);
      require(
        Token(takerOfferToken).transferFrom(_takerWallet, _makerWallet, _toMakerAmount)
      );
      assert(
        __tokenAndWalletBalancesMatch__(_makerWallet, _takerWallet, takerOfferToken)
      );

    // Ether to the maker and tokens to the taker
    } else if (takerOfferToken == address(0)) {
      _makerWallet.transfer(_toMakerAmount);
      require(
        Token(makerOfferToken).transferFrom(_makerWallet, _takerWallet, _toTakerAmount)
      );
      assert(
        __tokenAndWalletBalancesMatch__(_makerWallet, _takerWallet, makerOfferToken)
      );

    // Something went wrong one had to have been ether
    } else revert();

    return true;
  }

  /**
   * @dev compute the log10 of a given number, takes the floor, ie. 2.5 = 2
   * @param _number The number to compute the log 10 of.
   * @return The floored log 10.
   */
  function __flooredLog10__(uint _number)
    public
    constant
    returns (uint256)
  {
    uint unit = 0;
    while (_number / (10**unit) >= 10)
      unit++;
    return unit;
  }

  /**
   * @dev Calculates Keccak-256 hash of order with specified parameters.
   * @param _token_and_EOA_Addresses The addresses of the order, [makerEOA, makerOfferToken, makerWantToken].
   * @param _amountsExpirationAndSalt The amount of tokens as well as
   * the block number at which this order expires and random salt number.
   * @return Keccak-256 hash of each order.
   */
  function __generateOrderHashes__(
    address[4] _token_and_EOA_Addresses,
    uint256[8] _amountsExpirationAndSalt
  ) private
    constant
    returns (bytes32, bytes32)
  {
    bytes32 makerOrderHash = keccak256(
      address(this),
      _token_and_EOA_Addresses[0], // _makerEOA
      _token_and_EOA_Addresses[1], // offerToken
      _amountsExpirationAndSalt[0],  // offerTokenAmount
      _token_and_EOA_Addresses[3], // wantToken
      _amountsExpirationAndSalt[1],  // wantTokenAmount
      _amountsExpirationAndSalt[4], // expiry
      _amountsExpirationAndSalt[5] // salt
    );


    bytes32 takerOrderHash = keccak256(
      address(this),
      _token_and_EOA_Addresses[2], // _makerEOA
      _token_and_EOA_Addresses[3], // offerToken
      _amountsExpirationAndSalt[2],  // offerTokenAmount
      _token_and_EOA_Addresses[1], // wantToken
      _amountsExpirationAndSalt[3],  // wantTokenAmount
      _amountsExpirationAndSalt[6], // expiry
      _amountsExpirationAndSalt[7] // salt
    );

    return (makerOrderHash, takerOrderHash);
  }

  /**
   * @dev Returns the price ratio for this order.
   * The ratio is calculated with the largest value as the numerator, this aids
   * to significantly reduce rounding errors.
   * @param _makerOrder The maker order data structure.
   * @return The ratio to `_decimals` decimal places.
   */
  function __getOrderPriceRatio__(Order _makerOrder, uint256 _decimals)
    private
    constant
    returns (uint256 orderPriceRatio)
  {
    if (_makerOrder.offerTokenTotal_ >= _makerOrder.wantTokenTotal_) {
      orderPriceRatio = _makerOrder.offerTokenTotal_.mul(10**_decimals).div(_makerOrder.wantTokenTotal_);
    } else {
      orderPriceRatio = _makerOrder.wantTokenTotal_.mul(10**_decimals).div(_makerOrder.offerTokenTotal_);
    }
  }

  /**
   * @dev Compute the tradeable amounts of the two verified orders.
   * Token amount is the min remaining between want and offer of the two orders that isn't ether.
   * Ether amount is then: etherAmount = tokenAmount * priceRatio, as ratio = eth / token.
   * @param _makerOrder The maker order data structure.
   * @param _takerOrder The taker order data structure.
   * @return The amount moving from makerOfferRemaining to takerWantRemaining and vice versa.
   * TODO: consider rounding errors, etc
   */
  function __getTradeAmounts__(
    Order _makerOrder,
    Order _takerOrder
  ) private
    constant
    returns (uint256 toTakerAmount, uint256 toMakerAmount)
  {
    bool ratioIsWeiPerTok = __ratioIsWeiPerTok__(_makerOrder);
    uint256 decimals = __flooredLog10__(__max__(_makerOrder.offerTokenTotal_, _makerOrder.wantTokenTotal_)) + 1;
    uint256 priceRatio = __getOrderPriceRatio__(_makerOrder, decimals);

    // Amount left for order to receive
    uint256 makerAmountLeftToReceive = _makerOrder.wantTokenTotal_.sub(_makerOrder.wantTokenReceived_);
    uint256 takerAmountLeftToReceive = _takerOrder.wantTokenTotal_.sub(_takerOrder.wantTokenReceived_);

    // wei/tok and taker receiving wei or tok/wei and taker receiving tok
    if (
        ratioIsWeiPerTok && _takerOrder.wantToken_ == address(0) ||
        !ratioIsWeiPerTok && _takerOrder.wantToken_ != address(0)
    ) {
      // In the case that the maker is offering more than the taker wants for the same quantity being offered
      // For example: maker offer 20 wei for 10 tokens but taker offers 10 tokens for 10 wei
      // Taker receives 20 wei for the 10 tokens, both orders filled
      if (
        _makerOrder.offerTokenRemaining_ > takerAmountLeftToReceive &&
        makerAmountLeftToReceive <= _takerOrder.offerTokenRemaining_
      ) {
        toTakerAmount = __max__(_makerOrder.offerTokenRemaining_, takerAmountLeftToReceive);
      } else {
        toTakerAmount = __min__(_makerOrder.offerTokenRemaining_, takerAmountLeftToReceive);
      }

      toMakerAmount = toTakerAmount.mul(10**decimals).div(priceRatio);

    // wei/tok and maker receiving wei or tok/wei and maker receiving tok
    } else {
      toMakerAmount = __min__(_takerOrder.offerTokenRemaining_, makerAmountLeftToReceive);
      toTakerAmount = toMakerAmount.mul(10**decimals).div(priceRatio);
    }
  }

  /**
   * @dev Return the maximum of two uints
   * @param _a Uint 1
   * @param _b Uint 2
   * @return The grater value or a if equal
   */
  function __max__(uint256 _a, uint256 _b)
    private
    constant
    returns (uint256)
  {
    return _a < _b ? _b : _a;
  }

  /**
   * @dev Return the minimum of two uints
   * @param _a Uint 1
   * @param _b Uint 2
   * @return The smallest value or b if equal
   */
  function __min__(uint256 _a, uint256 _b)
    private
    constant
    returns (uint256)
  {
    return _a < _b ? _a : _b;
  }

  /**
   * @dev Define if the ratio to be used is wei/tok to tok/wei. Largest uint will
   * always act as the numerator.
   * @param _makerOrder The maker order object.
   * @return If the ratio is wei/tok or not.
   */
  function __ratioIsWeiPerTok__(Order _makerOrder)
    private
    constant
    returns (bool)
  {
    bool offerIsWei = _makerOrder.offerToken_ == address(0) ? true : false;

    // wei/tok
    if (offerIsWei && _makerOrder.offerTokenTotal_ >= _makerOrder.wantTokenTotal_) {
      return true;

    } else if (!offerIsWei && _makerOrder.wantTokenTotal_ >= _makerOrder.offerTokenTotal_) {
      return true;

    // tok/wei. otherwise wanting wei && offer > want, OR offer wei && want > offer
    } else {
      return false;
    }
  }

  /**
   * @dev Confirm that the orders do match and are valid.
   * @param _makerOrder The maker order data structure.
   * @param _takerOrder The taker order data structure.
   * @return Bool if the orders passes all checks.
   */
  function __ordersMatch_and_AreVaild__(
    Order _makerOrder,
    Order _takerOrder
  ) private
    constant
    returns (bool)
  {
    // Orders still active
    if (!_makerOrder.active_)
      return error('Maker order is inactive, Exchange.__ordersMatch_and_AreVaild__()');

    if (!_takerOrder.active_)
      return error('Taker order is inactive, Exchange.__ordersMatch_and_AreVaild__()');

    // Confirm tokens match
    // NOTE potentially omit as matching handled upstream?
    if (_makerOrder.wantToken_ != _takerOrder.offerToken_)
      return error('Maker wanted token does not match taker offer token, Exchange.__ordersMatch_and_AreVaild__()');

    if (_makerOrder.offerToken_ != _takerOrder.wantToken_)
      return error('Maker offer token does not match taker wanted token, Exchange.__ordersMatch_and_AreVaild__()');

    // Price Ratios, to x decimal places hence * decimals, dependent on the size of the denominator.
    // Ratios are relative to eth, amount of ether for a single token, ie. ETH / GNO == 0.2 Ether per 1 Gnosis
    uint256 orderPrice;  // The price the maker is willing to accept
    uint256 offeredPrice; // The offer the taker has given
    uint256 decimals = _makerOrder.offerToken_ == address(0) ? __flooredLog10__(_makerOrder.wantTokenTotal_) : __flooredLog10__(_makerOrder.offerTokenTotal_);

    // Ratio = larger amount / smaller amount
    if (_makerOrder.offerTokenTotal_ >= _makerOrder.wantTokenTotal_) {
      orderPrice = _makerOrder.offerTokenTotal_.mul(10**decimals).div(_makerOrder.wantTokenTotal_);
      offeredPrice = _takerOrder.wantTokenTotal_.mul(10**decimals).div(_takerOrder.offerTokenTotal_);

      // ie. Maker is offering 10 ETH for 100 GNO but taker is offering 100 GNO for 20 ETH, no match!
      // The taker wants more ether than the maker is offering.
      if (orderPrice < offeredPrice)
        return error('Taker price is greater than maker price, Exchange.__ordersMatch_and_AreVaild__()');

    } else {
      orderPrice = _makerOrder.wantTokenTotal_.mul(10**decimals).div(_makerOrder.offerTokenTotal_);
      offeredPrice = _takerOrder.offerTokenTotal_.mul(10**decimals).div(_takerOrder.wantTokenTotal_);

      // ie. Maker is offering 100 GNO for 10 ETH but taker is offering 5 ETH for 100 GNO, no match!
      // The taker is not offering enough ether for the maker
      if (orderPrice > offeredPrice)
        return error('Taker price is less than maker price, Exchange.__ordersMatch_and_AreVaild__()');

    }

    return true;
  }

  /**
   * @dev Ask each wallet to verify this order.
   * @param _token_and_EOA_Addresses The addresses of the maker and taker EOAs and offered token contracts.
   * [makerEOA, makerOfferToken, takerEOA, takerOfferToken]
   * @param _toMakerAmount The amount of tokens to be sent to the maker.
   * @param _toTakerAmount The amount of tokens to be sent to the taker.
   * @param _makerWallet The maker's wallet contract.
   * @param _takerWallet The taker's wallet contract.
   * @param _fee The fee to be paid for this trade, paid in full by taker.
   * @return Success if both wallets verify the order.
   */
  function __ordersVerifiedByWallets__(
    address[4] _token_and_EOA_Addresses,
    uint256 _toMakerAmount,
    uint256 _toTakerAmount,
    Wallet _makerWallet,
    Wallet _takerWallet,
    uint256 _fee
  ) private
    constant
    returns (bool)
  {
    // Have the transaction verified by both maker and taker wallets
    // confirm sufficient balance to transfer, offerToken and offerTokenAmount
    if(!_makerWallet.verifyOrder(_token_and_EOA_Addresses[1], _toTakerAmount, 0, 0))
      return error('Maker wallet could not verify the order, Exchange.__ordersVerifiedByWallets__()');

    if(!_takerWallet.verifyOrder(_token_and_EOA_Addresses[3], _toMakerAmount, _fee, edoToken_))
      return error('Taker wallet could not verify the order, Exchange.__ordersVerifiedByWallets__()');

    return true;
  }

  /**
   * @dev On chain verification of an ECDSA ethereum signature.
   * @param _signer The EOA address of the account that supposedly signed the message.
   * @param _orderHash The on-chain generated hash for the order.
   * @param _v ECDSA signature parameter v.
   * @param _r ECDSA signature parameter r.
   * @param _s ECDSA signature parameter s.
   * @return Bool if the signature is valid or not.
   */
  function __signatureIsValid__(
    address _signer,
    bytes32 _orderHash,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) private
    constant
    returns (bool)
  {
    address recoveredAddr = ecrecover(
      keccak256('\x19Ethereum Signed Message:\n32', _orderHash),
      _v, _r, _s
    );

    return recoveredAddr == _signer;
  }

  /**
   * @dev Confirm wallet local balances and token balances match.
   * @param _makerWallet  Maker wallet address.
   * @param _takerWallet  Taker wallet address.
   * @param _token  Token address to confirm balances match.
   * @return If the balances do match.
   */
  function __tokenAndWalletBalancesMatch__(
    address _makerWallet,
    address _takerWallet,
    address _token
  ) private
    constant
    returns(bool)
  {
    if (Token(_token).balanceOf(_makerWallet) != Wallet(_makerWallet).balanceOf(_token))
      return false;

    if (Token(_token).balanceOf(_takerWallet) != Wallet(_takerWallet).balanceOf(_token))
      return false;

    return true;
  }

  /**
   * @dev Update the order structs.
   * @param _makerOrder The maker order data structure.
   * @param _takerOrder The taker order data structure.
   * @param _toTakerAmount The amount of tokens to be moved to the taker.
   * @param _toTakerAmount The amount of tokens to be moved to the maker.
   * @return Success if the update succeeds.
   */
  function __updateOrders__(
    Order _makerOrder,
    Order _takerOrder,
    uint256 _toTakerAmount,
    uint256 _toMakerAmount
  ) private
  {
    // taker => maker
    _makerOrder.wantTokenReceived_ = _makerOrder.wantTokenReceived_.add(_toMakerAmount);
    _takerOrder.offerTokenRemaining_ = _takerOrder.offerTokenRemaining_.sub(_toMakerAmount);

    // maker => taker
    _takerOrder.wantTokenReceived_ = _takerOrder.wantTokenReceived_.add(_toTakerAmount);
    _makerOrder.offerTokenRemaining_ = _makerOrder.offerTokenRemaining_.sub(_toTakerAmount);
  }
}