removed methods for using ETH for collateral for time being

/*
    Copyright 2017 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity 0.4.18;

import "./Creatable.sol";
import "./oraclize/oraclizeAPI.sol";
import "./libraries/MathLib.sol";
import "./libraries/HashLib.sol";
import "zeppelin-solidity/contracts/token/ERC20.sol";
import "zeppelin-solidity/contracts/token/SafeERC20.sol";

// TODO:
//      add failsafe for pool distribution.
//      push as much into library as possible
//      think about circuit breaker in case of issues
//      discuss build out ability to use ETH vs only ERC20? - WETH?
//      add open interest to allow users to see outstanding open positions

/// @title MarketContract first example of a MarketProtocol contract using Oraclize services
/// @author Phil Elsasser <phil@marketprotcol.io>
contract MarketContract is Creatable, usingOraclize  {
    using MathLib for uint256;
    using MathLib for int;
    using HashLib for address;
    using SafeERC20 for ERC20;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint8 makerSide;            // 0=Buy 1=Sell
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN_ADDRESS;
    ERC20 public BASE_TOKEN;
    uint public PRICE_CAP;
    uint public PRICE_FLOOR;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public QTY_DECIMAL_PLACES;     // how many tradeable units make up a whole pricing increment
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT;
    uint8 constant public BUY_SIDE = 0;
    uint8 constant public SELL_SIDE = 1;
    uint constant public COST_PER_QUERY = 2 finney;    // leave static for now, price of first query from oraclize is 0
    uint constant public QUERY_CALLBACK_GAS = 300000;

    // state variables
    string public lastPriceQueryResult;
    uint public lastPrice;
    uint public settlementPrice;
    bool public isSettled;
    mapping(bytes32 => bool) validQueryIDs;

    // accounting
    mapping(address => UserNetPosition) addressToUserPosition;
    mapping(address => uint) userAddressToAccountBalance;   // stores account balances allowed to be allocated to orders
    mapping (bytes32 => int) public filledOrderQty;
    mapping (bytes32 => int) public cancelledOrderQty;
    uint collateralPoolBalance = 0;                         // current balance of all collateral committed

    // events
    event OracleQuerySuccess();
    event OracleQueryFailed();
    event UpdatedLastPrice(string price);
    event ContractSettled();
    event UpdatedUserBalance(address user, uint balance);
    event UpdatedPoolBalance(uint balance);

    /// @param contractName viewable name of this contract (BTC/ETH, LTC/ETH, etc)
    /// @param baseTokenAddress address of the ERC20 token that will be used for collateral and pricing
    /// @param oracleDataSource a data-source such as "URL", "WolframAlpha", "IPFS"
    /// see http://docs.oraclize.it/#ethereum-quick-start-simple-query
    /// @param oracleQuery see http://docs.oraclize.it/#ethereum-quick-start-simple-query for examples
    /// @param oracleQueryRepeatSeconds how often to repeat this callback to check for settlement, more frequent
    /// queries require more gas and may not be needed.
    /// @param floorPrice minimum tradeable price of this contract, contract enters settlement if breached
    /// @param capPrice maximum tradeable price of this contract, contract enters settlement if breached
    /// @param priceDecimalPlaces number of decimal places to convert our queried price from a floating point to
    /// an integer
    /// @param qtyDecimalPlaces decimal places to multiply traded qty by.
    /// @param secondsToExpiration - second from now that this contract expires and enters settlement
    function MarketContract(
        string contractName,
        address baseTokenAddress,
        string oracleDataSource,
        string oracleQuery,
        uint oracleQueryRepeatSeconds,
        uint floorPrice,
        uint capPrice,
        uint priceDecimalPlaces,
        uint qtyDecimalPlaces,
        uint secondsToExpiration
    ) public payable {

        require(capPrice > floorPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN_ADDRESS = baseTokenAddress;
        BASE_TOKEN = ERC20(baseTokenAddress);
        PRICE_CAP = capPrice;
        PRICE_FLOOR = floorPrice;
        EXPIRATION = now + secondsToExpiration;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        ORACLE_QUERY_REPEAT = oracleQueryRepeatSeconds;
        PRICE_DECIMAL_PLACES = priceDecimalPlaces;
        QTY_DECIMAL_PLACES = qtyDecimalPlaces;
        require(checkSufficientStartingBalance(secondsToExpiration));
        queryOracle();  // schedules recursive calls to oracle
    }

    /*
    // EXTERNAL METHODS
    */

    // @param userAddress address to return position for
    // @return the users current open position.
    function getUserPosition(address userAddress) external view returns (int)  {
        return addressToUserPosition[userAddress].netPosition;
    }

    /// @notice deposits tokens to the smart contract to fund the user account and provide needed tokens for collateral
    /// pool upon trade matching.
    /// @param depositAmount qty of ERC20 tokens to deposit to the smart contract to cover open orders and collateral
    function depositTokensForTrading(uint256 depositAmount) external {
        // user must call approve!
        BASE_TOKEN.safeTransferFrom(msg.sender, this, depositAmount);
        uint256 balanceAfterDeposit = userAddressToAccountBalance[msg.sender].add(depositAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterDeposit;
        UpdatedUserBalance(msg.sender, balanceAfterDeposit);
    }

    // @notice called by a participant wanting to trade a specific order
    /// @param maker address of the maker of this order
    /// @param taker address of the designated taker or address(0) if publicly available to be traded
    /// @param feeRecipient address of a fee recipient, optional
    /// @param makerFee amount of MKT token for maker fee
    /// @param takerFee amount of MKT token for taker fee
    /// @param price of the order
    /// @param orderQty quantity of the order
    /// @param qtyToFill quantity taker is willing to fill (max)
    /// @param expirationTimeStamp last valid timestamp of the order (seconds since epoch)
    /// @param v order signature
    /// @param r order signature
    /// @param s order signature
    function tradeOrder(
        address maker,
        address taker,
        address feeRecipient,
        uint makerFee,
        uint takerFee,
        uint price,
        int orderQty,
        int qtyToFill,
        uint expirationTimeStamp,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(!isSettled);                                // no trading past settlement
        require(taker == address(0) || taker == msg.sender);// taker can be anyone, or specifically the caller!
        require(maker != address(0) && maker != taker);     // do not allow self trade
        require(orderQty != 0 && qtyToFill != 0);           // no zero trades

        bytes32 orderHash = address(this).createOrderHash(maker, feeRecipient,
                                makerFee, takerFee, orderQty, price, expirationTimeStamp);

        require(maker.isValidSignature(orderHash, v, r, s));

        if(now >= expirationTimeStamp) {
            // TODO: order is expired, no trade - log this
            return;
        }
        // TODO finish build out

    }

    // @notice called by the maker of an order to attempt to cancel the order before its expiration time stamp
    /// @param maker address of the maker of this order
    /// @param taker address of the designated taker or address(0) if publicly available to be traded
    /// @param feeRecipient address of a fee recipient, optional
    /// @param makerFee amount of MKT token for maker fee
    /// @param takerFee amount of MKT token for taker fee
    /// @param price of the order
    /// @param orderQty quantity of the order
    /// @param qtyToCancel quantity maker is attempting to cancel
    /// @param expirationTimeStamp last valid timestamp of the order (seconds since epoch)
    function cancelOrder(
        address maker,
        address taker,
        address feeRecipient,
        uint makerFee,
        uint takerFee,
        uint price,
        int orderQty,
        int qtyToCancel,
        uint expirationTimeStamp
    ) external {
        require(maker == msg.sender);                                       // only maker can cancel standing order
        require(qtyToCancel != 0 && qtyToCancel.isSameSign(orderQty));      // cannot cancel 0 and signs must match

        if(now >= expirationTimeStamp || isSettled) {
            // TODO: order or contract is expired, no need to cancel - log this
            return;
        }

        bytes32 orderHash = address(this).createOrderHash(maker, feeRecipient,
                                makerFee, takerFee, orderQty, price, expirationTimeStamp);
        // TODO: check to see if order is fully filled order cancelled
        //
        cancelledOrderQty[orderHash] = cancelledOrderQty[orderHash].add(qtyToCancel);
        // TODO: log
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    function settleAndClose() external {
        require(isSettled);
        UserNetPosition storage userNetPos = addressToUserPosition[msg.sender];
        if(userNetPos.netPosition != 0) {
            // this user has a position that we need to settle based upon the settlement price of the contract
            reduceUserNetPosition(msg.sender, userNetPos.netPosition * - 1, settlementPrice);
        }
        withdrawTokens(userAddressToAccountBalance[msg.sender]);   // transfer all balances back to user.
    }

    /*
    // PUBLIC METHODS
    */

    /// @param queryID of the returning query, this should match our own internal mapping
    /// @param result query to be processed
    /// @param proof result proof
    function __callback(bytes32 queryID, string result, bytes proof) public {
        require(validQueryIDs[queryID]);
        require(msg.sender == oraclize_cbAddress());
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isSettled) {
            queryOracle();  // set up our next query
        }
    }

    /// @notice returns the qty that is no longer available to trade for a given order
    /// @param orderHash hash of order to find filled and cancelled qty
    /// @return int quantity that is no longer able to filled from the supplied order hash
    function getQtyFilledOrCancelledFromOrder(bytes32 orderHash) public view returns (int) {
        return filledOrderQty[orderHash].add(cancelledOrderQty[orderHash]);
    }

    /// @notice removes token from users trading account
    /// @param withdrawAmount qty of token to attempt to withdraw
    function withdrawTokens(uint256 withdrawAmount) public {
        require(userAddressToAccountBalance[msg.sender] >= withdrawAmount);   // ensure sufficient balance
        uint256 balanceAfterWithdrawal = userAddressToAccountBalance[msg.sender].subtract(withdrawAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterWithdrawal;   // update balance before external call!
        BASE_TOKEN.safeTransfer(msg.sender, withdrawAmount);
        UpdatedUserBalance(msg.sender, balanceAfterWithdrawal);
    }

    /*
    // PRIVATE METHODS
    */

    /// @param maker address of the maker in the trade
    /// @param taker address of the taker in the trade
    /// @param qty quantity transacted between parties
    /// @param price agreed price of the matched trade.
    function updatePositions(address maker, address taker, int qty, uint price) private {
        updatePosition(maker, qty, price);
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(taker, qty * -1, price);

    }

    /// @param userAddress storage struct containing position information for this user
    /// @param qty signed quantity this users position is changing by, + for buy and - for sell
    /// @param price transacted price of the new position / trade
    function updatePosition(address userAddress, int qty, uint price) private {
        UserNetPosition storage userNetPosition = addressToUserPosition[userAddress];
        if(userNetPosition.netPosition == 0 ||  userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos, no collateral returned
            userNetPosition.positions.push(Position(price, qty)); //append array with new position
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                userNetPosition.positions.push(Position(price, newNetPos));   // append array with new position
            }
        }
        userNetPosition.netPosition.add(qty);
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(address userAddress, int qty, uint price) private {
        uint neededCollateral = MathLib.calculateNeededCollateral(this, qty, price);
        commitCollateralToPool(userAddress, neededCollateral);
    }

    /// @param userAddress address of user who is reducing their pos
    /// @param userNetPos storage struct for this users position
    /// @param qty signed quantity of the qty to reduce this users position by
    /// @param price transacted price
    function reduceUserNetPosition(
        address userAddress,
        UserNetPosition storage userNetPos,
        int qty,
        uint price
    ) private {
        uint collateralToReturnToUserAccount = 0;
        int qtyToReduce = qty;                      // note: this sign is opposite of our users position
        assert(userNetPos.positions.length != 0);   // sanity check
        while(qtyToReduce != 0) {   //TODO: ensure we dont run out of gas here!
            Position storage position = userNetPos.positions[userNetPos.positions.length - 1];  // get the last pos (LIFO)
            if(position.qty.abs() <= qtyToReduce.abs()) {   // this position is completely consumed!
                collateralToReturnToUserAccount.add(MathLib.calculateNeededCollateral(this, position.qty, price));
                qtyToReduce = qtyToReduce.add(position.qty);
                userNetPos.positions.length--;  // remove this position from our array.
            }
            else {  // this position stays, just reduce the qty.
                position.qty = position.qty.add(qtyToReduce);
                // pos is opp sign of qty we are reducing here!
                collateralToReturnToUserAccount.add(MathLib.calculateNeededCollateral(this, qtyToReduce * -1, price));
                //qtyToReduce = 0; // completely reduced now!
                break;
            }
        }

        if(collateralToReturnToUserAccount != 0) {  // allocate funds back to user acct.
            withdrawCollateralFromPool(userAddress, collateralToReturnToUserAccount);
        }
    }

    /// @notice moves collateral from a user's account to the pool upon trade execution.
    /// @param fromAddress address of user entering trade
    /// @param collateralAmount amount of collateral to transfer from user account to collateral pool
    function commitCollateralToPool(address fromAddress, uint collateralAmount) private {
        require(userAddressToAccountBalance[fromAddress] >= collateralAmount);   // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[fromAddress].subtract(collateralAmount);
        userAddressToAccountBalance[fromAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.add(collateralAmount);
        UpdatedUserBalance(fromAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @notice withdraws collateral from pool to a user account upon exit or trade settlement
    /// @param toAddress address of user
    /// @param collateralAmount amount to transfer from pool to user.
    function withdrawCollateralFromPool(address toAddress, uint collateralAmount) private {
        require(collateralPoolBalance >= collateralAmount); // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[toAddress].add(collateralAmount);
        userAddressToAccountBalance[toAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.subtract(collateralAmount);
        UpdatedUserBalance(toAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    function queryOracle() private {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            OracleQueryFailed();
            lastPriceQueryResult = "FAILED"; //TODO: failsafe
        } else {
            OracleQuerySuccess();
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY, QUERY_CALLBACK_GAS);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() private {
        if(isSettled)   // already settled.
            return;

        if(now > EXPIRATION) {  // note: miners can cheat this by small increments of time (minutes, not hours)
            isSettled = true;   // time based expiration has occurred.
        } else if(lastPrice >= PRICE_CAP || lastPrice <= PRICE_FLOOR) {
            isSettled = true;   // we have breached/touched our pricing bands
        }

        if(isSettled) {
            settleContract();
        }
    }

    function settleContract(uint finalSettlementPrice) private {
        settlementPrice = finalSettlementPrice;
        ContractSettled();
    }

    // for now lets require alot of padding for the settlement,
    function checkSufficientStartingBalance(uint secondsToExpiration) private view returns (bool isSufficient) {
        //uint costPerQuery = oraclize_getPrice(ORACLE_DATA_SOURCE); this doesn't work prior to first query(its free)
        uint expectedNoOfQueries = secondsToExpiration / ORACLE_QUERY_REPEAT;
        uint approxGasRequired = COST_PER_QUERY * expectedNoOfQueries;
        return this.balance > (approxGasRequired * 2);
    }
}

/*
    Copyright 2017 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity 0.4.18;

import "./Creatable.sol";
import "./oraclize/oraclizeAPI.sol";
import "./libraries/MathLib.sol";
import "./libraries/HashLib.sol";
import "zeppelin-solidity/contracts/token/ERC20.sol";
import "zeppelin-solidity/contracts/token/SafeERC20.sol";

//TODO: fix style issues
//      add failsafe for pool distribution.
//      push as much into library as possible
//      think about circuit breaker in case of issues
//      do we want to use ETH or WETH for ETH based contract?
//      add open interest to allow users to see outstanding open positions

/// @title MarketContract first example of a MarketProtocol contract using Oraclize services
/// @author Phil Elsasser <phil@marketprotcol.io>
contract MarketContract is Creatable, usingOraclize  {
    using MathLib for uint256;
    using MathLib for int;
    using HashLib for address;
    using SafeERC20 for ERC20;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint8 makerSide;            // 0=Buy 1=Sell
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN_ADDRESS;
    ERC20 public BASE_TOKEN;
    uint public PRICE_CAP;
    uint public PRICE_FLOOR;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public QTY_DECIMAL_PLACES;     // how many tradeable units make up a whole pricing increment
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT;
    uint8 constant public BUY_SIDE = 0;
    uint8 constant public SELL_SIDE = 1;
    uint constant public COST_PER_QUERY = 2 finney;    // leave static for now, price of first query from oraclize is 0
    uint constant public QUERY_CALLBACK_GAS = 300000;

    // state variables
    string public lastPriceQueryResult;
    uint public lastPrice;
    uint public settlementPrice;
    bool public isSettled;
    mapping(bytes32 => bool) validQueryIDs;

    // accounting
    mapping(address => UserNetPosition) addressToUserPosition;
    mapping(address => uint) userAddressToAccountBalance;   // stores account balances allowed to be allocated to orders
    mapping (bytes32 => int) public filledOrderQty;
    mapping (bytes32 => int) public cancelledOrderQty;
    uint collateralPoolBalance = 0;                         // current balance of all collateral committed

    // events
    event OracleQuerySuccess();
    event OracleQueryFailed();
    event UpdatedLastPrice(string price);
    event ContractSettled();
    event UpdatedUserBalance(address user, uint balance);
    event UpdatedPoolBalance(uint balance);

    /// @param contractName viewable name of this contract (BTC/ETH, LTC/ETH, etc)
    /// @param baseTokenAddress address of the ERC20 token that will be used for collateral and pricing
    /// @param oracleDataSource a data-source such as "URL", "WolframAlpha", "IPFS"
    /// see http://docs.oraclize.it/#ethereum-quick-start-simple-query
    /// @param oracleQuery see http://docs.oraclize.it/#ethereum-quick-start-simple-query for examples
    /// @param oracleQueryRepeatSeconds how often to repeat this callback to check for settlement, more frequent
    /// queries require more gas and may not be needed.
    /// @param floorPrice minimum tradeable price of this contract, contract enters settlement if breached
    /// @param capPrice maximum tradeable price of this contract, contract enters settlement if breached
    /// @param priceDecimalPlaces number of decimal places to convert our queried price from a floating point to
    /// an integer
    /// @param qtyDecimalPlaces decimal places to multiply traded qty by.
    /// @param secondsToExpiration - second from now that this contract expires and enters settlement
    function MarketContract(
        string contractName,
        address baseTokenAddress,
        string oracleDataSource,
        string oracleQuery,
        uint oracleQueryRepeatSeconds,
        uint floorPrice,
        uint capPrice,
        uint priceDecimalPlaces,
        uint qtyDecimalPlaces,
        uint secondsToExpiration
    ) public payable {

        require(capPrice > floorPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN_ADDRESS = baseTokenAddress;
        BASE_TOKEN = ERC20(baseTokenAddress);
        PRICE_CAP = capPrice;
        PRICE_FLOOR = floorPrice;
        EXPIRATION = now + secondsToExpiration;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        ORACLE_QUERY_REPEAT = oracleQueryRepeatSeconds;
        PRICE_DECIMAL_PLACES = priceDecimalPlaces;
        QTY_DECIMAL_PLACES = qtyDecimalPlaces;
        require(checkSufficientStartingBalance(secondsToExpiration));
        queryOracle();  // schedules recursive calls to oracle
    }

    /*
    // EXTERNAL METHODS
    */

    // @param userAddress address to return position for
    // @return the users current open position.
    function getUserPosition(address userAddress) external view returns (int)  {
        return addressToUserPosition[userAddress].netPosition;
    }

    function depositEtherForTrading() external payable {
        // should we allow ether or force users to use WETH?
    }

    /// @notice deposits tokens to the smart contract to fund the user account and provide needed tokens for collateral
    /// pool upon trade matching.
    /// @param depositAmount qty of ERC20 tokens to deposit to the smart contract to cover open orders and collateral
    function depositTokensForTrading(uint256 depositAmount) external {
        // user must call approve!
        BASE_TOKEN.safeTransferFrom(msg.sender, this, depositAmount);
        uint256 balanceAfterDeposit = userAddressToAccountBalance[msg.sender].add(depositAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterDeposit;
        UpdatedUserBalance(msg.sender, balanceAfterDeposit);
    }

    // @notice called by a participant wanting to trade a specific order
    /// @param maker address of the maker of this order
    /// @param taker address of the designated taker or address(0) if publicly available to be traded
    /// @param feeRecipient address of a fee recipient, optional
    /// @param makerFee amount of MKT token for maker fee
    /// @param takerFee amount of MKT token for taker fee
    /// @param price of the order
    /// @param orderQty quantity of the order
    /// @param qtyToFill quantity taker is willing to fill (max)
    /// @param expirationTimeStamp last valid timestamp of the order (seconds since epoch)
    /// @param v order signature
    /// @param r order signature
    /// @param s order signature
    function tradeOrder(
        address maker,
        address taker,
        address feeRecipient,
        uint makerFee,
        uint takerFee,
        uint price,
        int orderQty,
        int qtyToFill,
        uint expirationTimeStamp,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(!isSettled);                                // no trading past settlement
        require(taker == address(0) || taker == msg.sender);// taker can be anyone, or specifically the caller!
        require(maker != address(0) && maker != taker);     // do not allow self trade
        require(orderQty != 0 && qtyToFill != 0);           // no zero trades

        bytes32 orderHash = address(this).createOrderHash(maker, feeRecipient,
                                makerFee, takerFee, orderQty, price, expirationTimeStamp);

        require(maker.isValidSignature(orderHash, v, r, s));

        if(now >= expirationTimeStamp) {
            // TODO: order is expired, no trade - log this
            return;
        }
        // TODO finish build out

    }

    // @notice called by the maker of an order to attempt to cancel the order before its expiration time stamp
    /// @param maker address of the maker of this order
    /// @param taker address of the designated taker or address(0) if publicly available to be traded
    /// @param feeRecipient address of a fee recipient, optional
    /// @param makerFee amount of MKT token for maker fee
    /// @param takerFee amount of MKT token for taker fee
    /// @param price of the order
    /// @param orderQty quantity of the order
    /// @param qtyToCancel quantity maker is attempting to cancel
    /// @param expirationTimeStamp last valid timestamp of the order (seconds since epoch)
    function cancelOrder(
        address maker,
        address taker,
        address feeRecipient,
        uint makerFee,
        uint takerFee,
        uint price,
        int orderQty,
        int qtyToCancel,
        uint expirationTimeStamp
    ) external {
        require(maker == msg.sender);                                       // only maker can cancel standing order
        require(qtyToCancel != 0 && qtyToCancel.isSameSign(orderQty));      // cannot cancel 0 and signs must match

        if(now >= expirationTimeStamp || isSettled) {
            // TODO: order or contract is expired, no need to cancel - log this
            return;
        }

        bytes32 orderHash = address(this).createOrderHash(maker, feeRecipient,
                                makerFee, takerFee, orderQty, price, expirationTimeStamp);
        // TODO: check to see if order is fully filled order cancelled
        //
        cancelledOrderQty[orderHash] = cancelledOrderQty[orderHash].add(qtyToCancel);
        // TODO: log
    }

    // @notice called by a user after settlement has occurred.  This function will finalize all accounting around any
    // outstanding positions and return all remaining collateral to the caller. This should only be called after
    // settlement has occurred.
    function settleAndClose() external {
        require(isSettled);
        UserNetPosition storage userNetPos = addressToUserPosition[msg.sender];
        if(userNetPos.netPosition != 0) {
            // this user has a position that we need to settle based upon the settlement price of the contract
            reduceUserNetPosition(msg.sender, userNetPos.netPosition * - 1, settlementPrice);
        }
        withdrawTokens(userAddressToAccountBalance[msg.sender]);   // transfer all balances back to user.
    }

    /*
    // PUBLIC METHODS
    */

    /// @param queryID of the returning query, this should match our own internal mapping
    /// @param result query to be processed
    /// @param proof result proof
    function __callback(bytes32 queryID, string result, bytes proof) public {
        require(validQueryIDs[queryID]);
        require(msg.sender == oraclize_cbAddress());
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isSettled) {
            queryOracle();  // set up our next query
        }
    }

    /// @notice returns the qty that is no longer available to trade for a given order
    /// @param orderHash hash of order to find filled and cancelled qty
    /// @return int quantity that is no longer able to filled from the supplied order hash
    function getQtyFilledOrCancelledFromOrder(bytes32 orderHash) public view returns (int) {
        return filledOrderQty[orderHash].add(cancelledOrderQty[orderHash]);
    }

    /// @notice removes token from users trading account
    /// @param withdrawAmount qty of token to attempt to withdraw
    function withdrawTokens(uint256 withdrawAmount) public {
        require(userAddressToAccountBalance[msg.sender] >= withdrawAmount);   // ensure sufficient balance
        uint256 balanceAfterWithdrawal = userAddressToAccountBalance[msg.sender].subtract(withdrawAmount);
        userAddressToAccountBalance[msg.sender] = balanceAfterWithdrawal;   // update balance before external call!
        BASE_TOKEN.safeTransfer(msg.sender, withdrawAmount);
        UpdatedUserBalance(msg.sender, balanceAfterWithdrawal);
    }

    /*
    // PRIVATE METHODS
    */

    /// @param maker address of the maker in the trade
    /// @param taker address of the taker in the trade
    /// @param qty quantity transacted between parties
    /// @param price agreed price of the matched trade.
    function updatePositions(address maker, address taker, int qty, uint price) private {
        updatePosition(maker, qty, price);
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(taker, qty * -1, price);

    }

    /// @param userAddress storage struct containing position information for this user
    /// @param qty signed quantity this users position is changing by, + for buy and - for sell
    /// @param price transacted price of the new position / trade
    function updatePosition(address userAddress, int qty, uint price) private {
        UserNetPosition storage userNetPosition = addressToUserPosition[userAddress];
        if(userNetPosition.netPosition == 0 ||  userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos, no collateral returned
            userNetPosition.positions.push(Position(price, qty)); //append array with new position
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                userNetPosition.positions.push(Position(price, newNetPos));   // append array with new position
            }
        }
        userNetPosition.netPosition.add(qty);
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(address userAddress, int qty, uint price) private {
        uint neededCollateral = MathLib.calculateNeededCollateral(this, qty, price);
        commitCollateralToPool(userAddress, neededCollateral);
    }

    /// @param userAddress address of user who is reducing their pos
    /// @param userNetPos storage struct for this users position
    /// @param qty signed quantity of the qty to reduce this users position by
    /// @param price transacted price
    function reduceUserNetPosition(
        address userAddress,
        UserNetPosition storage userNetPos,
        int qty,
        uint price
    ) private {
        uint collateralToReturnToUserAccount = 0;
        int qtyToReduce = qty;                      // note: this sign is opposite of our users position
        assert(userNetPos.positions.length != 0);   // sanity check
        while(qtyToReduce != 0) {   //TODO: ensure we dont run out of gas here!
            Position storage position = userNetPos.positions[userNetPos.positions.length - 1];  // get the last pos (LIFO)
            if(position.qty.abs() <= qtyToReduce.abs()) {   // this position is completely consumed!
                collateralToReturnToUserAccount.add(MathLib.calculateNeededCollateral(this, position.qty, price));
                qtyToReduce = qtyToReduce.add(position.qty);
                userNetPos.positions.length--;  // remove this position from our array.
            }
            else {  // this position stays, just reduce the qty.
                position.qty = position.qty.add(qtyToReduce);
                // pos is opp sign of qty we are reducing here!
                collateralToReturnToUserAccount.add(MathLib.calculateNeededCollateral(this, qtyToReduce * -1, price));
                //qtyToReduce = 0; // completely reduced now!
                break;
            }
        }

        if(collateralToReturnToUserAccount != 0) {  // allocate funds back to user acct.
            withdrawCollateralFromPool(userAddress, collateralToReturnToUserAccount);
        }
    }

    /// @notice moves collateral from a user's account to the pool upon trade execution.
    /// @param fromAddress address of user entering trade
    /// @param collateralAmount amount of collateral to transfer from user account to collateral pool
    function commitCollateralToPool(address fromAddress, uint collateralAmount) private {
        require(userAddressToAccountBalance[fromAddress] >= collateralAmount);   // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[fromAddress].subtract(collateralAmount);
        userAddressToAccountBalance[fromAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.add(collateralAmount);
        UpdatedUserBalance(fromAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    /// @notice withdraws collateral from pool to a user account upon exit or trade settlement
    /// @param toAddress address of user
    /// @param collateralAmount amount to transfer from pool to user.
    function withdrawCollateralFromPool(address toAddress, uint collateralAmount) private {
        require(collateralPoolBalance >= collateralAmount); // ensure sufficient balance
        uint newBalance = userAddressToAccountBalance[toAddress].add(collateralAmount);
        userAddressToAccountBalance[toAddress] = newBalance;
        collateralPoolBalance = collateralPoolBalance.subtract(collateralAmount);
        UpdatedUserBalance(toAddress, newBalance);
        UpdatedPoolBalance(collateralPoolBalance);
    }

    function queryOracle() private {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            OracleQueryFailed();
            lastPriceQueryResult = "FAILED"; //TODO: failsafe
        } else {
            OracleQuerySuccess();
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY, QUERY_CALLBACK_GAS);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() private {
        if(isSettled)   // already settled.
            return;

        if(now > EXPIRATION) {  // note: miners can cheat this by small increments of time (minutes, not hours)
            isSettled = true;   // time based expiration has occurred.
        } else if(lastPrice >= PRICE_CAP || lastPrice <= PRICE_FLOOR) {
            isSettled = true;   // we have breached/touched our pricing bands
        }

        if(isSettled) {
            settleContract();
        }
    }

    function settleContract(uint finalSettlementPrice) private {
        settlementPrice = finalSettlementPrice;
        ContractSettled();
    }

    // for now lets require alot of padding for the settlement,
    function checkSufficientStartingBalance(uint secondsToExpiration) private view returns (bool isSufficient) {
        //uint costPerQuery = oraclize_getPrice(ORACLE_DATA_SOURCE); this doesn't work prior to first query(its free)
        uint expectedNoOfQueries = secondsToExpiration / ORACLE_QUERY_REPEAT;
        uint approxGasRequired = COST_PER_QUERY * expectedNoOfQueries;
        return this.balance > (approxGasRequired * 2);
    }
}


Add comment about totalSupply

Add comment about totalSupply

contract Token {
    /* This is a slight change to the ERC20 base standard.
    function totalSupply() constant returns (uint256 supply);
    is replaced with:
    uint256 public totalSupply;
    This automatically creates a getter function for the totalSupply.
    This is moved to the base contract since public getter functions are not
    currently recognised as an implementation of the matching abstract
    function by the compiler.
    */
    /// total amount of tokens
    uint256 public totalSupply;

    /// @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);
}

contract Token {
    /// total amount of tokens
    uint256 public totalSupply;

    /// @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);
}


exit() to remove self from whitelist

exit() to remove self from whitelist

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

contract Registry {

    struct Params {
        uint minDeposit;        // minimum deposit for listing to be whitelisted
        uint applyStage;        // length of period in which applicants wait to be whitelisted
        uint dispensationPct;   // percentage of losing party's deposit distributed to winning party
        uint commitPeriodLen;   // length of commit period for voting
        uint revealPeriodLen;   // length of reveal period for voting
        uint voteQuorum;        // type of majority out of 100 necessary for vote success
    }

    struct Listing {
        uint applicationExpiry; // expiration date of apply stage
        bool whitelisted;       // indicates registry status
        address owner;          // owner of Listing
        uint currentDeposit;    // number of tokens staked
        uint challengeID;       // identifier of canonical challenge
    }

    struct Challenge {
        // uint rewardPool;        // pool of tokens distributed amongst winning voters
        address challenger;     // owner of Challenge
        bool resolved;          // indication of if challenge is resolved
        uint stake;             // number of tokens at risk for either party during challenge
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) challengeMap;

    // maps domainHashes to associated listing data
    mapping(bytes32 => Listing) public listingMap;

    //Global Variables
    Params canonicalParams;
    StandardToken token;
    PLCRVoting voting;

    // ------------
    // CONSTRUCTOR:
    // ------------

    function Registry(
        address _tokenAddr,
        uint _minDeposit,
        uint _applyStageLength,
        uint _commitPeriodLength,
        uint _revealPeriodLength,
        uint _dispensationPct,
        uint _voteQuorum
    ) {
        token = StandardToken(_tokenAddr);
        voting = new PLCRVoting(_tokenAddr);

        canonicalParams = Params({
            minDeposit: _minDeposit,
            applyStage: _applyStageLength,
            dispensationPct: _dispensationPct,
            commitPeriodLen: _commitPeriodLength,
            revealPeriodLen: _revealPeriodLength,
            voteQuorum: _voteQuorum
        });
    }

    // --------------------
    // PUBLISHER INTERFACE:
    // --------------------

    function apply(string domain) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));

        Listing listing = listingMap[sha3(domain)];
        listing.owner = msg.sender;

        uint minDeposit = canonicalParams.minDeposit;
        require(token.transferFrom(listing.owner, this, minDeposit));

        listing.applicationExpiry = block.timestamp + canonicalParams.applyStage;
        listing.currentDeposit = minDeposit;
    }

    //Allows the owner of a domain in the listing to increase their deposit
    function deposit(string domain, uint amount) external {
        Listing listing = listingMap[sha3(domain)];

        require(listing.owner == msg.sender);
        require(token.transferFrom(msg.sender, this, amount));

        listing.currentDeposit += amount;
    }

    //Allows the owner of a domain in the listing to withdraw unlocked tokens
    //The publisher's domain remains whitelisted
    function withdraw(string domain, uint amount) external {
        Listing listing = listingMap[sha3(domain)];

        require(listing.owner == msg.sender);
        //must maintain at least minDeposit number of tokens locked
        require(amount <= listing.currentDeposit - canonicalParams.minDeposit);
        require(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;
    }

    //Allows the owner of a domain to remove the domain from the whitelist
    //Returns all tokens to the owner
    function exit(string domain) external {
        Listing listing = listingMap[sha3(domain)];

        require(isWhitelisted(domain));
        require(listing.owner == msg.sender);
        require(token.transfer(msg.sender, listing.currentDeposit));

        resetListing(domain);
    }

    // -----------------------
    // TOKEN HOLDER INTERFACE:
    // -----------------------

    function challenge(string domain) external returns (uint challengeID) {
        bytes32 domainHash = sha3(domain);
        Listing listing = listingMap[domainHash];

        require(appExists(domain) || listing.whitelisted);
        require(challengeMap[listing.challengeID].resolved);     // prevent multiple challenges

        if (listing.currentDeposit < canonicalParams.minDeposit) {
            resetListing(domain);
            return 0;               // publisher was auto-delisted
        }

        uint deposit = canonicalParams.minDeposit;
        require(token.transferFrom(msg.sender, this, deposit));

        uint pollID = voting.startPoll(domain,
            canonicalParams.voteQuorum,
            canonicalParams.commitPeriodLen,
            canonicalParams.revealPeriodLen
        );

        challengeMap[pollID] = Challenge({
            challenger: msg.sender,
            // rewardPool: ((100 - canonicalParams.dispensationPct) * deposit) / 100
            stake: deposit,
            resolved: false
        });

        listingMap[domainHash].challengeID = pollID;      // update listing to store most recent challenge
        listingMap[domainHash].currentDeposit -= deposit; // lock tokens for listing during challenge

        return pollID;
    }

    function updateStatus(string domain) public {
        bytes32 domainHash = sha3(domain);
        uint challengeID = listingMap[domainHash].challengeID;
        require(!challengeMap[challengeID].resolved);  // require processed flag to be false

        // IF NO CHALLENGE AFTER APPLY STAGE
        if (challengeID == 0 && isExpired(listingMap[domainHash].applicationExpiry)) {
            listingMap[domainHash].whitelisted = true;
        } else {
            uint stake = challengeMap[challengeID].stake;

            if (voting.isPassed(challengeID)) {
                listingMap[domainHash].whitelisted = true;
                listingMap[domainHash].currentDeposit += stake; // give stake back to applicant
            } else {
                resetListing(domain); // whitelisted = false
                require(token.transfer(challengeMap[challengeID].challenger, stake)); // give stake to challenger
            }

            challengeMap[challengeID].resolved = true; // set flag on challenge being processed
        }
    }

    // --------
    // HELPERS:
    // --------

    function isWhitelisted(string domain) constant public returns (bool whitelisted) {
        return listingMap[sha3(domain)].whitelisted;
    }

    function appExists(string domain) constant public returns (bool exists) {
        return listingMap[sha3(domain)].applicationExpiry > 0;
    }

    function isExpired(uint termDate) constant public returns (bool expired) {
        return termDate > block.timestamp;
    }

    function resetListing(string domain) internal {
        bytes32 domainHash = sha3(domain);
        Listing listing = listingMap[domainHash];

        require(token.transfer(listing.owner, listing.currentDeposit));

        delete listingMap[domainHash];
    }
}

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

contract Registry {

    struct Params {
        uint minDeposit;        // minimum deposit for listing to be whitelisted
        uint applyStage;        // length of period in which applicants wait to be whitelisted
        uint dispensationPct;   // percentage of losing party's deposit distributed to winning party
        uint commitPeriodLen;   // length of commit period for voting
        uint revealPeriodLen;   // length of reveal period for voting
        uint voteQuorum;        // type of majority out of 100 necessary for vote success
    }

    struct Listing {
        uint applicationExpiry; // expiration date of apply stage
        bool whitelisted;       // indicates registry status
        address owner;          // owner of Listing
        uint currentDeposit;    // number of tokens staked
        uint challengeID;       // identifier of canonical challenge
    }

    struct Challenge {
        // uint rewardPool;        // pool of tokens distributed amongst winning voters
        address challenger;     // owner of Challenge
        bool resolved;          // indication of if challenge is resolved
        uint stake;             // number of tokens at risk for either party during challenge
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) challengeMap;

    // maps domainHashes to associated listing data
    mapping(bytes32 => Listing) public listingMap;

    // ------------
    // CONSTRUCTOR:
    // ------------

    Params canonicalParams;
    StandardToken token;
    PLCRVoting voting;

    function Registry(
        address _tokenAddr,
        uint _minDeposit,
        uint _applyStageLength,
        uint _commitPeriodLength,
        uint _revealPeriodLength,
        uint _dispensationPct,
        uint _voteQuorum
    ) {
        token = StandardToken(_tokenAddr);
        voting = new PLCRVoting(_tokenAddr);

        canonicalParams = Params({
            minDeposit: _minDeposit,
            applyStage: _applyStageLength,
            dispensationPct: _dispensationPct,
            commitPeriodLen: _commitPeriodLength,
            revealPeriodLen: _revealPeriodLength,
            voteQuorum: _voteQuorum
        });
    }

    // --------------------
    // PUBLISHER INTERFACE:
    // --------------------

    function apply(string domain) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));

        Listing listing = listingMap[sha3(domain)];
        listing.owner = msg.sender;

        uint minDeposit = canonicalParams.minDeposit;
        require(token.transferFrom(listing.owner, this, minDeposit));

        listing.applicationExpiry = block.timestamp + canonicalParams.applyStage;
        listing.currentDeposit = minDeposit;
    }

    function deposit(string domain, uint amount) external {
        Listing listing = listingMap[sha3(domain)];

        require(listing.owner == msg.sender);
        require(token.transferFrom(msg.sender, this, amount));

        listing.currentDeposit += amount;
    }

    function withdraw(string domain, uint amount) external {
        Listing listing = listingMap[sha3(domain)];

        require(listing.owner == msg.sender);
        require(amount <= listing.currentDeposit);
        require(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;
    }

    // -----------------------
    // TOKEN HOLDER INTERFACE:
    // -----------------------

    function challenge(string domain) external returns (uint challengeID) {
        bytes32 domainHash = sha3(domain);
        Listing listing = listingMap[domainHash];

        require(appExists(domain) || listing.whitelisted);
        require(challengeMap[listing.challengeID].resolved);     // prevent multiple challenges

        if (listing.currentDeposit < canonicalParams.minDeposit) {
            resetListing(domain);
            return 0;               // publisher was auto-delisted
        }

        uint deposit = canonicalParams.minDeposit;
        require(token.transferFrom(msg.sender, this, deposit));

        uint pollID = voting.startPoll(domain,
            canonicalParams.voteQuorum,
            canonicalParams.commitPeriodLen,
            canonicalParams.revealPeriodLen
        );

        challengeMap[pollID] = Challenge({
            challenger: msg.sender,
            // rewardPool: ((100 - canonicalParams.dispensationPct) * deposit) / 100
            stake: deposit,
            resolved: false
        });

        listingMap[domainHash].challengeID = pollID;      // update listing to store most recent challenge
        listingMap[domainHash].currentDeposit -= deposit; // lock tokens for listing during challenge

        return pollID;
    }

    function updateStatus(string domain) public {
        bytes32 domainHash = sha3(domain);
        uint challengeID = listingMap[domainHash].challengeID;
        require(!challengeMap[challengeID].resolved);  // require processed flag to be false

        // IF NO CHALLENGE AFTER APPLY STAGE
        if (challengeID == 0 && isExpired(listingMap[domainHash].applicationExpiry)) {
            listingMap[domainHash].whitelisted = true;
        } else {
            uint stake = challengeMap[challengeID].stake;

            if (voting.isPassed(challengeID)) {
                listingMap[domainHash].whitelisted = true;
                listingMap[domainHash].currentDeposit += stake; // give stake back to applicant
            } else {
                resetListing(domain); // whitelisted = false
                require(token.transfer(challengeMap[challengeID].challenger, stake)); // give stake to challenger
            }

            challengeMap[challengeID].resolved = true; // set flag on challenge being processed
        }
    }

    // --------
    // HELPERS:
    // --------

    function isWhitelisted(string domain) constant public returns (bool whitelisted) {
        return listingMap[sha3(domain)].whitelisted;
    }

    function appExists(string domain) constant public returns (bool exists) {
        return listingMap[sha3(domain)].applicationExpiry > 0;
    }

    function isExpired(uint termDate) constant public returns (bool expired) {
        return termDate > block.timestamp;
    }

    function resetListing(string domain) internal {
        bytes32 domainHash = sha3(domain);
        Listing listing = listingMap[domainHash];

        require(token.transfer(listing.owner, listing.currentDeposit));

        delete listingMap[domainHash];
    }
}


Ensuring factory has append-only mapping

contract MicropaymentsChannel {
    enum Stage {
        Empty,
        PartiallyConfirmed,
        Confirmed,
        Closing,
        Closed
    }

    uint public constant closingBlockDelay = 10;

    Stage public stage;
    uint public id;
    address public from;
    uint public fromBalance;
    address public to;
    uint public toBalance;
    uint public balanceTimestamp;

    uint public closingBlockNumber;

    function MicropaymentsChannel(address _from, address _to, uint _id) {
        if (_id == 0) {
            throw;
        }
        stage = Stage.Empty;
        id  = _id;
        from = _from;
        fromBalance = 0;
        to = _to;
        toBalance = 0;
        balanceTimestamp = 0;
    }

    modifier onlyFrom {
        if (msg.sender != from) {
            throw;
        }
        _
    }

   modifier onlyTo {
        if (msg.sender != to) {
            throw;
        }
        _
    }

    modifier onlyParticipants {
        if ((msg.sender != from) && (msg.sender != to)) {
            throw;
        }
        _
    }

    modifier atStage(Stage _stage) {
        if (stage != _stage) {
            throw;
        }
        _
    }

    modifier atOneOfStages(Stage _stage1, Stage _stage2) {
        if ((stage != _stage1) && (stage != _stage2)) {
            throw;
        }
        _
    }

    modifier readyToClose {
        if (block.number < closingBlockNumber) {
            throw;
        }
        _
    }

    function assertYoungerBalance(uint _balanceTimestamp) internal {
        if (_balanceTimestamp <= balanceTimestamp) {
            throw;
        }
    }

    function assertSaneBalance(uint _fromBalance, uint _toBalance) internal {
        if (fromBalance + toBalance < _fromBalance + _toBalance) {
            throw;
        }
    }

    function assertMessageHash(bytes32 _expected, bytes32 _sigHash) internal {
        if (_expected != _sigHash) {
            throw;
        }
    }

    function assertSignedByBoth(
        bytes32 _sigHash,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    ) internal {
        address signer = getSigner(_sigHash, _sigV, _sigR, _sigS);
        if (!(((from == msg.sender) && (to == signer)) ||
              ((from == signer) && (to == msg.sender)))) {
            throw;
        }
    }

    function getUpdateHash(
        uint _balanceTimestamp,
        uint _fromBalance,
        uint _toBalance
    ) constant returns(bytes32) {
        return sha3(id, _balanceTimestamp, _fromBalance, _toBalance);
    }

    function getSigner(
        bytes32 _sigHash,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    ) constant returns(address) {
        return ecrecover(_sigHash, _sigV, _sigR, _sigS);
    }

    function openChannel()
        onlyFrom
        atStage(Stage.Empty)
    {
        stage = Stage.PartiallyConfirmed;
        fromBalance = msg.value;
    }

    function confirmChannel()
        onlyTo
        atStage(Stage.PartiallyConfirmed)
    {
        stage = Stage.Confirmed;
        toBalance = msg.value;
    }

    function requestClosingChannel()
        onlyParticipants
        atOneOfStages(Stage.PartiallyConfirmed, Stage.Confirmed)
    {
        if (stage == Stage.PartiallyConfirmed) {
            stage = Stage.Closed;
        } else {
            stage = Stage.Closing;
            closingBlockNumber = block.number + closingBlockDelay;
        }
    }

    function closeChannel()
        onlyParticipants
        atStage(Stage.Closing)
        readyToClose
    {
        stage = Stage.Closed;
    }

    function withdrawFrom()
        onlyFrom
        atStage(Stage.Closed)
    {
        uint balance = fromBalance;
        fromBalance = 0;
        from.send(balance);
    }

    function withdrawTo()
        onlyTo
        atStage(Stage.Closed)
    {
        uint toSend = toBalance;
        toBalance = 0;
        to.send(balance);
    }

    function updateChannelState(
        uint _balanceTimestamp,
        uint _fromBalance,
        uint _toBalance,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    )
        onlyParticipants
        atOneOfStages(Stage.Confirmed, Stage.Closing)
    {
        assertYoungerBalance(_balanceTimestamp);
        assertSaneBalance(_fromBalance, _toBalance);
        assertSignedByBoth(getUpdateHash(_balanceTimestamp, _fromBalance, _toBalance), _sigV, _sigR, _sigS);
        balanceTimestamp = _balanceTimestamp;
        fromBalance = _fromBalance;
        toBalance = _toBalance;
    }
}

contract MicropaymentsNetwork {
    mapping (address => mapping (address => mapping( uint => MicropaymentsChannel))) channels;

    function MicropaymentsNetwork() {
    }

    function assertSlotIsAvailable(address _from, address _to, uint _id) {
        if (channels[_from][_to][_id] != 0) {
            throw;
        }
    }

    function registerChannel(address _from, address _to, uint _id) {
        assertSlotIsAvailable(_from, _to, _id);
        channels[_from][_to][_id] = new MicropaymentsChannel(_from, _to, _id);
    }

    function getChannel(address _from, address _to, uint _id)
        constant
        returns (MicropaymentsChannel)
    {
        return channels[_from][_to][_id];
    }
}

contract MicropaymentsChannel {
    enum Stage {
        Empty,
        PartiallyConfirmed,
        Confirmed,
        Closing,
        Closed
    }

    uint public constant closingBlockDelay = 10;

    Stage public stage;
    uint public id;
    address public from;
    uint public fromBalance;
    address public to;
    uint public toBalance;
    uint public balanceTimestamp;

    uint public closingBlockNumber;

    function MicropaymentsChannel(address _from, address _to, uint _id) {
        stage = Stage.Empty;
        id  = _id;
        from = _from;
        fromBalance = 0;
        to = _to;
        toBalance = 0;
        balanceTimestamp = 0;
    }

    modifier onlyFrom {
        if (msg.sender != from) {
            throw;
        }
        _
    }

   modifier onlyTo {
        if (msg.sender != to) {
            throw;
        }
        _
    }

    modifier onlyParticipants {
        if ((msg.sender != from) && (msg.sender != to)) {
            throw;
        }
        _
    }

    modifier atStage(Stage _stage) {
        if (stage != _stage) {
            throw;
        }
        _
    }

    modifier atOneOfStages(Stage _stage1, Stage _stage2) {
        if ((stage != _stage1) && (stage != _stage2)) {
            throw;
        }
        _
    }

    modifier readyToClose() {
        if (block.number < closingBlockNumber) {
            throw;
        }
        _
    }

    function assertYoungerBalance(uint _balanceTimestamp) internal {
        if (_balanceTimestamp <= balanceTimestamp) {
            throw;
        }
    }

    function assertSaneBalance(uint _fromBalance, uint _toBalance) internal {
        if (fromBalance + toBalance < _fromBalance + _toBalance) {
            throw;
        }
    }

    function assertMessageHash(bytes32 _expected, bytes32 _sigHash) internal {
        if (_expected != _sigHash) {
            throw;
        }
    }

    function assertSignedByBoth(
        bytes32 _sigHash,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    ) internal {
        address signer = getSigner(_sigHash, _sigV, _sigR, _sigS);
        if (!(((from == msg.sender) && (to == signer)) ||
              ((from == signer) && (to == msg.sender)))) {
            throw;
        }
    }

    function getUpdateHash(
        uint _balanceTimestamp,
        uint _fromBalance,
        uint _toBalance
    ) constant returns(bytes32) {
        return sha3(id, _balanceTimestamp, _fromBalance, _toBalance);
    }

    function getSigner(
        bytes32 _sigHash,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    ) constant returns(address) {
        return ecrecover(_sigHash, _sigV, _sigR, _sigS);
    }

    function openChannel()
        onlyFrom
        atStage(Stage.Empty)
    {
        stage = Stage.PartiallyConfirmed;
        fromBalance = msg.value;
    }

    function confirmChannel()
        onlyTo
        atStage(Stage.PartiallyConfirmed)
    {
        stage = Stage.Confirmed;
        toBalance = msg.value;
    }

    function requestClosingChannel()
        onlyParticipants
        atOneOfStages(Stage.PartiallyConfirmed, Stage.Confirmed)
    {
        if (stage == Stage.PartiallyConfirmed) {
            stage = Stage.Closed;
        } else {
            stage = Stage.Closing;
            closingBlockNumber = block.number + closingBlockDelay;
        }
    }

    function closeChannel()
        onlyParticipants
        atStage(Stage.Closing)
        readyToClose
    {
        stage = Stage.Closed;
    }

    function withdrawFrom()
        onlyFrom
        atStage(Stage.Closed)
    {
        uint balance = fromBalance;
        fromBalance = 0;
        from.send(balance);
    }

    function withdrawTo()
        onlyTo
        atStage(Stage.Closed)
    {
        uint toSend = toBalance;
        toBalance = 0;
        to.send(balance);
    }

    function updateChannelState(
        uint _balanceTimestamp,
        uint _fromBalance,
        uint _toBalance,
        uint8 _sigV,
        bytes32 _sigR,
        bytes32 _sigS
    )
        onlyParticipants
        atOneOfStages(Stage.Confirmed, Stage.Closing)
    {
        assertYoungerBalance(_balanceTimestamp);
        assertSaneBalance(_fromBalance, _toBalance);
        assertSignedByBoth(getUpdateHash(_balanceTimestamp, _fromBalance, _toBalance), _sigV, _sigR, _sigS);
        balanceTimestamp = _balanceTimestamp;
        fromBalance = _fromBalance;
        toBalance = _toBalance;
    }
}

contract MicropaymentsNetwork {
    mapping (address => mapping (address => mapping( uint => MicropaymentsChannel))) channels;

    function MicropaymentsNetwork() {
    }

    function registerChannel(address _from, address _to, uint _id) {
        channels[_from][_to][_id] = new MicropaymentsChannel(_from, _to, _id);
    }

    function getChannel(address _from, address _to, uint _id)
        constant
        returns (MicropaymentsChannel)
    {
        return channels[_from][_to][_id];
    }
}


pragma solidity ^0.4.2;

// ----------------------------------------------------------------------------------------------
// Basic ERC20 Token Contract For **TESTING ONLY** on Testnet or Dev blockchain.
//
// Enjoy. (c) Bok Consulting Pty Ltd 2016. The MIT Licence.
// ----------------------------------------------------------------------------------------------

// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/issues/20
contract ERC20Interface {

    // Get the total token supply
    function totalSupply() constant returns (uint256 totalSupply);

    // Get the account balance of another account with address _owner
    function balanceOf(address _owner) constant returns (uint256 balance);

    // Send _value amount of tokens to address _to
    function transfer(address _to, uint256 _value) returns (bool success);

    // Send _value amount of tokens from address _from to address _to
    // The transferFrom method is used for a withdraw workflow, allowing contracts to send
    // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
    // fees in sub-currencies; the command should fail unless the _from account has
    // deliberately authorized the sender of the message via some mechanism; we propose
    // these standardized APIs for approval:
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);

    // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
    // If this function is called again it overwrites the current allowance with _value.
    function approve(address _spender, uint256 _value) returns (bool success);

    // Returns the amount which _spender is still allowed to withdraw from _owner
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);

    // Triggered when tokens are transferred.
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    // Triggered whenever approve(address _spender, uint256 _value) is called.
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

contract ERC20Token is ERC20Interface {

    // Owner of this contract
    address public owner;

    // Balances for each account
    mapping(address => uint256) balances;

    // Owner of account approves the transfer of an amount to another account
    mapping(address => mapping (address => uint256)) allowed;

    // Total supply
    uint256 _totalSupply;

    // Functions with this modifier can only be executed by the bank
    modifier onlyOwner() {
        if (msg.sender != owner) {
            throw;
        }
        _;
    }

    // Constructor
    function ERC20Token(address _owner) {
        owner = _owner;
    }

    function totalSupply() constant returns (uint256 totalSupply) {
        totalSupply = _totalSupply;
    }

    // What is the balance of a particular account?
    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
    }

    // Transfer the balance from owner's account to another account
    function transfer(address _to, uint256 _amount) returns (bool success) {
        if (balances[msg.sender] >= _amount && _amount > 0) {
            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(msg.sender, _to, _amount);
            return true;
        } else {
           return false;
        }
    }

    // Send _value amount of tokens from address _from to address _to
    // The transferFrom method is used for a withdraw workflow, allowing contracts to send
    // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
    // fees in sub-currencies; the command should fail unless the _from account has
    // deliberately authorized the sender of the message via some mechanism; we propose
    // these standardized APIs for approval:
    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    ) returns (bool success) {

        if (balances[_from] >= _amount
            && allowed[_from][msg.sender] >= _amount
            && _amount > 0) {

            balances[_to] += _amount;
            balances[_from] -= _amount;
            allowed[_from][msg.sender] -= _amount;
            Transfer(_from, _to, _amount);
            return true;
        } else {
            return false;
        }
    }

    // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
    // If this function is called again it overwrites the current allowance with _value.
    function approve(address _spender, uint256 _amount) returns (bool success) {
        allowed[msg.sender][_spender] = _amount;
        Approval(msg.sender, _spender, _amount);
        return true;
    }

    function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }

    function () payable {
        if (msg.value > 0) {
            _totalSupply += msg.value;
            balances[msg.sender] += msg.value;
        }
    }

    function withdrawEthers(uint256 ethers) onlyOwner returns (bool ok) {
        if (this.balance >= ethers) {
            return owner.send(ethers);
        }
    }

    // Triggered when fiat currency is converted to tokens
    event TokensCreated(address indexed _owner, uint256 _amount);
}

error: pathspec 'contracts/TestERC20Token.sol' did not match any file(s) known to git


proper comments

proper comments

pragma solidity ^0.4.8;

// This contract implements a simple store that can interact with
// registered customers. Every customer has its own shopping cart.
// @title Contract Store
// @author Harris Brakmic
contract Store {

    /* Store internals */
    address owner;      // store owner's address
    bytes32 public name; // store name
    uint balance;       // store's balance

    mapping (address => Customer) customers;
    mapping (uint => Product) products;

    /* Store Events */
    event CustomerRegistered(address customer);
    event CustomerRegistrationFailed(address customer);
    event CustomerDeregistered(address customer);
    event CustomerDeregistrationFailed(address customer);

    event ProductRegistered(uint productId);
    event ProductDeregistered(uint productId);
    event ProductRegistrationFailed(uint productId);
    event ProductDeregistrationFaled(uint productId);

    event CartProductInserted(address customer, uint prodId);
    event CartProductInsertionFailed(address customer, uint prodId);
    event CartProductRemoved(address customer, uint prodId);
    event CartCheckoutCompleted(address customer, uint paymentSum);
    event CartCheckoutFailed(address customer, uint paymentSum);

    modifier onlyStoreOwner {
        require(msg.sender == owner);
        _;
    }

    // every customer has an address, name,
    // balance and a shopping cart
    struct Customer {
        address adr;
        bytes name;
        uint balance;
        Cart cart;
    }
    // A shopping cart contains an array of product ids: @products
    // and a sum of product prices: @completeSum
    // The @completeSum gets automatically updated when customer
    // adds or removes products.
    struct Cart {
      uint[] products;
      uint completeSum;
    }
    // Represents a product:
    // Product id: @id
    // Product name: @name
    // Decription: @description
    // Amount of items in a single product: @default_amount
    struct Product {
        uint id;
        bytes32 name;
        bytes32 description;
        uint price;
        uint default_amount;
    }
    // Represents a receipt [NOT IN USE]
    struct Receipt {
        InvoiceLine[] lines;
        address customer_address;
    }
    // Represents a single entry describing a single product [NOT IN USE]
    struct InvoiceLine {
        bytes product_id;
        uint amount;
        uint product_price;
        uint total_price;
    }
    /**
          @dev Default constructor
    */
    function Store() {
        owner = msg.sender;
        name = "retailtest";
    }

    /**
          @dev Changes the address of the store owner

          @param new_owner
          @return success

    */
    function changeOwner(address new_owner) onlyStoreOwner
                                                        returns (bool success) {
      if (new_owner != owner) {
        owner = new_owner;
        return true;
      }
      return false;
    }

    /**
          @dev Register a single product

          @param id
          @param name
          @param description
          @param price
          @param default_amount

          @return success
    */
    function registerProduct(uint id, bytes32 name, bytes32 description,
                             uint price, uint default_amount)
                                         onlyStoreOwner returns (bool success) {
        var product = Product(id, name, description, price, default_amount);
        if (checkProductValidity(product)) {
            products[id] = product;
            ProductRegistered(id);
            return true;
        }
        ProductRegistrationFailed(id);
        return false;
    }

    /**
          @dev Removes a product from the list

          @param productId
          @return success
    */
    function deregisterProduct(uint productId) onlyStoreOwner
                                                        returns (bool success) {
      Product product = products[productId];
      if (product.id == productId) {
        delete products[productId];
        ProductDeregistered(productId);
        return true;
      }
      ProductDeregistrationFaled(productId);
      return false;
    }

    /**
          @dev Returns a elements describing a product
          @param id
          @return (prod_name, prod_desc, prod_price, prod_default_amount)
    */
    function getProduct(uint id) constant returns (bytes32 prod_name,
                                                   bytes32 prod_desc,
                                                   uint prod_price,
                                                   uint prod_default_amount) {
       return (products[id].name,
               products[id].description,
               products[id].price,
               products[id].default_amount);
    }

    /**
          @dev Registers a new customer (only store owners)

          @param _address
          @param _name
          @param _balance
          @return success
    */
    function registerCustomer(address _address, bytes _name, uint _balance)
                                        onlyStoreOwner returns (bool success) {
      if (_address != owner) {
        customers[_address] = Customer(_address, _name, _balance,
                                                        Cart(new uint[](0), 0));
        CustomerRegistered(_address);
        return true;
      }
      CustomerRegistrationFailed(_address);
      return false;
    }

    /**
        @dev Removes a customer (only store owners)

        @param customerAddress
        @return success
    */
    function deregisterCustomer(address customerAddress) onlyStoreOwner
                                                        returns (bool success) {
      Customer customer = customers[customerAddress];
      if (customer.adr != address(0)) {
        delete customers[customerAddress];
        CustomerDeregistered(customerAddress);
        return true;
      }
      CustomerDeregistrationFailed(customerAddress);
      return false;
    }

    /**
        @dev Inserts a product into the shopping cart.
            This function returns a boolean and the position of the
            inserted product.
            The positional information can later be used to directly reference
            the product within the mapping. Solidity mappings aren't interable.

        @param prodId
        @return (success, position)
    */
    function insertProductIntoCart(uint prodId) returns (bool success,
                                                         uint position) {
      /*if (msg.sender != owner) {*/
        customers[msg.sender].cart.products.push(prodId);
        customers[msg.sender].cart.completeSum += products[prodId].price;
        CartProductInserted(msg.sender, prodId);
        return (true, customers[msg.sender].cart.products.length - 1);
      /*}*/
      /*CartProductInsertionFailed(msg.sender, prodId);*/
      /*return (false, 0);*/
    }

    /**
        @dev Removes a product entry from the shopping cart

        @param prodPosition
    */
    function removeProductFromCart(uint prodPosition) {
      if (msg.sender != owner) {
        uint[] memory new_product_list = new uint[](customers[msg.sender]
                                                    .cart.products.length - 1);
        var customerProds = customers[msg.sender].cart.products;
        for (uint i = 0; i < customerProds.length; i++) {
          if (i != prodPosition) {
            new_product_list[i] = customerProds[i];
          } else {
            customers[msg.sender].cart.completeSum -=
                                               products[customerProds[i]].price;
            CartProductRemoved(msg.sender, customerProds[i]);
          }
        }
        customers[msg.sender].cart.products = new_product_list;
      }
    }

    /**
        @dev Returns a list of product ids and a complete sum.
        The caller address must be a registered customer.

        @return (productIds, completeSum)
    */
    function getCart() constant returns (uint[] productIds, uint completeSum) {
      Customer customer = customers[msg.sender];
      uint len = customer.cart.products.length;
      uint[] memory ids = new uint[](len);
      for (uint i = 0; i < len; i++) {
        ids[i] = products[i].id;
      }
      return (ids, customer.cart.completeSum);
    }

    // Returns customer's balance
    function getBalance() constant returns (uint _balance) {
      return customers[msg.sender].balance;
    }

    /**
        @dev Invokes a checkout process that'll use the current shopping cart to
        transfer balances between the current customer and the store

        @return success
    */
    function checkoutCart() returns (bool success) {
      if (msg.sender != owner) {
        Customer customer = customers[msg.sender];
        uint paymentSum = customer.cart.completeSum;
        if ((customer.balance >= paymentSum) &&
            customer.cart.products.length > 0) {
            customer.balance -= paymentSum;
            customer.cart = Cart(new uint[](0), 0);
            balance += paymentSum;
            CartCheckoutCompleted(msg.sender, paymentSum);
            return true;
        }
      }
      CartCheckoutFailed(msg.sender, paymentSum);
      return false;
    }

    /**
          @dev Changes the name of the store

          @param new_store_name
          @return success
    */
    function renameStoreTo(bytes32 new_store_name) onlyStoreOwner
                                                        returns (bool success) {
        if (new_store_name.length != 0 &&
            new_store_name.length <= 32) {
            name = new_store_name;
            return true;
        }
        return false;
    }

    /**
          @dev Checks product validity

          @param product
          @return valid
    */
    function checkProductValidity(Product product) private
                                                          returns (bool valid) {
       return (product.price > 0);
    }

    /**
          @dev Payable fallback
    */
    function() payable { }
}

pragma solidity ^0.4.8;

// This contract implements a simple store that can interact with
// registered customers. Every customer has its own shopping cart.
// @title Contract Store
// @author Harris Brakmic
contract Store {

    /* Store internals */
    address owner;      // store owner's address
    bytes32 public name; // store name
    uint balance;       // store's balance

    mapping (address => Customer) customers;
    mapping (uint => Product) products;

    /* Store Events */
    event CustomerRegistered(address customer);
    event CustomerRegistrationFailed(address customer);
    event CustomerDeregistered(address customer);
    event CustomerDeregistrationFailed(address customer);

    event ProductRegistered(uint productId);
    event ProductDeregistered(uint productId);
    event ProductRegistrationFailed(uint productId);
    event ProductDeregistrationFaled(uint productId);

    event CartProductInserted(address customer, uint prodId);
    event CartProductInsertionFailed(address customer, uint prodId);
    event CartProductRemoved(address customer, uint prodId);
    event CartCheckoutCompleted(address customer, uint paymentSum);
    event CartCheckoutFailed(address customer, uint paymentSum);

    modifier onlyStoreOwner {
        require(msg.sender == owner);
        _;
    }

    // every customer has an address, name,
    // balance and a shopping cart
    struct Customer {
        address adr;
        bytes name;
        uint balance;
        Cart cart;
    }
    // A shopping cart contains an array of product ids: @products
    // and a sum of product prices: @completeSum
    // The @completeSum gets automatically updated when customer
    // adds or removes products.
    struct Cart {
      uint[] products;
      uint completeSum;
    }
    // Represents a product:
    // Product id: @id
    // Product name: @name
    // Decription: @description
    // Amount of items in a single product: @default_amount
    struct Product {
        uint id;
        bytes32 name;
        bytes32 description;
        uint price;
        uint default_amount;
    }
    // Represents a receipt [NOT IN USE]
    struct Receipt {
        InvoiceLine[] lines;
        address customer_address;
    }
    // Represents a single entry describing a single product [NOT IN USE]
    struct InvoiceLine {
        bytes product_id;
        uint amount;
        uint product_price;
        uint total_price;
    }
    // Default constructor
    function Store() {
        owner = msg.sender;
        name = "retailtest";
    }

    function changeOwner(address new_owner) onlyStoreOwner
                                                        returns (bool success) {
      if (new_owner != owner) {
        owner = new_owner;
        return true;
      }
      return false;
    }

    // Register a single product (only store owners)
    function registerProduct(uint id, bytes32 name, bytes32 description,
                             uint price, uint default_amount)
                                         onlyStoreOwner returns (bool success) {
        var product = Product(id, name, description, price, default_amount);
        if (checkProductValidity(product)) {
            products[id] = product;
            ProductRegistered(id);
            return true;
        }
        ProductRegistrationFailed(id);
        return false;
    }

    // Removes a product from the list (only store owners)
    function deregisterProduct(uint productId) onlyStoreOwner
                                                        returns (bool success) {
      Product product = products[productId];
      if (product.id == productId) {
        delete products[productId];
        ProductDeregistered(productId);
        return true;
      }
      ProductDeregistrationFaled(productId);
      return false;
    }

    // Returns a elements describing a product
    function getProduct(uint id) constant returns (bytes32 prod_name,
                                                   bytes32 prod_desc,
                                                   uint prod_price,
                                                   uint prod_default_amount) {
       return (products[id].name,
               products[id].description,
               products[id].price,
               products[id].default_amount);
    }

    // Registers a new customer (only store owners)
    function registerCustomer(address _address, bytes _name, uint _balance)
                                        onlyStoreOwner returns (bool success) {
      if (_address != owner) {
        customers[_address] = Customer(_address, _name, _balance,
                                                        Cart(new uint[](0), 0));
        CustomerRegistered(_address);
        return true;
      }
      CustomerRegistrationFailed(_address);
      return false;
    }

    // Removes a customer (only store owners)
    function deregisterCustomer(address customerAddress) onlyStoreOwner
                                                        returns (bool success) {
      Customer customer = customers[customerAddress];
      if (customer.adr != address(0)) {
        delete customers[customerAddress];
        CustomerDeregistered(customerAddress);
        return true;
      }
      CustomerDeregistrationFailed(customerAddress);
      return false;
    }

    // Inserts a product into the shopping cart (caller must be customer)
    // This function returns a boolean and the position of the inserted product.
    // The positional information can later be used to directly reference
    // the product within the mapping. Solidity mappings aren't interable.
    function insertProductIntoCart(uint prodId) returns (bool success,
                                                         uint position) {
      /*if (msg.sender != owner) {*/
        customers[msg.sender].cart.products.push(prodId);
        customers[msg.sender].cart.completeSum += products[prodId].price;
        CartProductInserted(msg.sender, prodId);
        return (true, customers[msg.sender].cart.products.length - 1);
      /*}*/
      /*CartProductInsertionFailed(msg.sender, prodId);*/
      /*return (false, 0);*/
    }

    // Removes a product entry from the shopping cart (caller must be a customer)
    function removeProductFromCart(uint prodPosition) {
      if (msg.sender != owner) {
        uint[] memory new_product_list = new uint[](customers[msg.sender]
                                                    .cart.products.length - 1);
        var customerProds = customers[msg.sender].cart.products;
        for (uint i = 0; i < customerProds.length; i++) {
          if (i != prodPosition) {
            new_product_list[i] = customerProds[i];
          } else {
            customers[msg.sender].cart.completeSum -=
                                               products[customerProds[i]].price;
            CartProductRemoved(msg.sender, customerProds[i]);
          }
        }
        customers[msg.sender].cart.products = new_product_list;
      }
    }

    // Returns a list of product ids and a complete sum for current customer
    // The caller address must be a registered customer
    function getCart() constant returns (uint[] productIds, uint completeSum) {
      Customer customer = customers[msg.sender];
      uint len = customer.cart.products.length;
      uint[] memory ids = new uint[](len);
      for (uint i = 0; i < len; i++) {
        ids[i] = products[i].id;
      }
      return (ids, customer.cart.completeSum);
    }

    // Returns customer's balance
    function getBalance() constant returns (uint _balance) {
      return customers[msg.sender].balance;
    }

    // Invokes a checkout process that'll use the current shopping cart to
    // transfer balances between the current customer and the store
    function checkoutCart() returns (bool success) {
      if (msg.sender != owner) {
        Customer customer = customers[msg.sender];
        uint paymentSum = customer.cart.completeSum;
        if ((customer.balance >= paymentSum) &&
            customer.cart.products.length > 0) {
            customer.balance -= paymentSum;
            customer.cart = Cart(new uint[](0), 0);
            balance += paymentSum;
            CartCheckoutCompleted(msg.sender, paymentSum);
            return true;
        }
      }
      CartCheckoutFailed(msg.sender, paymentSum);
      return false;
    }

    // Changes the name of the store (only store owners)
    function renameStoreTo(bytes32 new_store_name) onlyStoreOwner
                                                        returns (bool success) {
        if (new_store_name.length != 0 &&
            new_store_name.length <= 32) {
            name = new_store_name;
            return true;
        }
        return false;
    }

    // Checks product validity (private function)
    function checkProductValidity(Product product) private
                                                          returns (bool valid) {
       return (product.price > 0);
    }

    // Payable fallback
    function() payable { }
}