adding method to get users acct balance

adding method to get users acct balance

/*
    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/OrderLib.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
//      add function to allow user to pay for an extra query that could trigger settlement
//      require creator to own arbitrary amount of MEM token
//      add in fee functionality needed for nodes and the reduced fees for holders of MEM.

/// @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 OrderLib for address;
    using OrderLib for OrderLib.Order;
    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;
    }

    enum ErrorCodes {
        ORDER_EXPIRED,              // past designated timestamp
        ORDER_DEAD                  // order if fully filled or fully cancelled
    }

    // 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(uint settlePrice);
    event UpdatedUserBalance(address indexed user, uint balance);
    event UpdatedPoolBalance(uint balance);
    event Error(ErrorCodes indexed errorCode, bytes32 indexed orderHash);

    // order events
    event OrderFilled(
        address indexed maker,
        address indexed taker,
        address indexed feeRecipient,
        int filledQty,
        uint paidMakerFee,
        uint paidTakerFee,
        bytes32 orderHash // should this be indexed?
    );
    event OrderCancelled(
        address indexed maker,
        address indexed feeRecipient,
        int cancelledQty,
        bytes32 indexed orderHash
    );

    /// @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);
        // TODO: check for minimum MEM token balance of caller.
        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;
    }

    /// @param userAddress address of user
    /// @return the users currently unallocated token balance
    function getUserAccountBalance(address userAddress) external view returns (uint) {
        return userAddressToAccountBalance[userAddress];
    }

    /// @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 orderAddresses - maker, taker and feeRecipient addresses
    /// @param unsignedOrderValues makerFee, takerFree, price, expirationTimeStamp, and salt (for hashing)
    /// @param orderQty quantity of the order
    /// @param qtyToFill quantity taker is willing to fill of original order(max)
    /// @param v order signature
    /// @param r order signature
    /// @param s order signature
    function tradeOrder(
        address[3] orderAddresses,
        uint[5] unsignedOrderValues,
        int orderQty,
        int qtyToFill,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (int filledQty) {
        require(!isSettled);                                // no trading past settlement
        require(orderQty != 0 && qtyToFill != 0);           // no zero trades
        require(orderQty.isSameSign(qtyToFill));            // signs should match
        OrderLib.Order memory order = address(this).createOrder(orderAddresses, unsignedOrderValues, orderQty);

        // taker can be anyone, or specifically the caller!
        require(order.taker == address(0) || order.taker == msg.sender);
        // do not allow self trade
        require(order.maker != address(0) && order.maker != order.taker);
        require(order.maker.isValidSignature(order.orderHash, v, r, s));

        if(now >= order.expirationTimeStamp) {
            Error(ErrorCodes.ORDER_EXPIRED, order.orderHash);
            return 0;
        }

        int remainingQty = orderQty.subtract(getQtyFilledOrCancelledFromOrder(order.orderHash));
        if(remainingQty == 0) { // there is no qty remaining  - cannot fill!
            Error(ErrorCodes.ORDER_DEAD, order.orderHash);
            return 0;
        }

        filledQty = MathLib.absMin(remainingQty, qtyToFill);
        updatePositions(order.maker, order.taker, filledQty, order.price);

        uint paidMakerFee = 0;
        uint paidTakerFee = 0;

        if(order.feeRecipient != address(0)) { // we need to transfer fees to recipient

            if(order.makerFee > 0) {

            }

            if(order.takerFee > 0) {

            }
        }

        OrderFilled(
            order.maker,
            order.taker,
            order.feeRecipient,
            filledQty,
            paidMakerFee,
            paidTakerFee,
            order.orderHash
        );

        return filledQty;
    }

    /// @notice called by the maker of an order to attempt to cancel the order before its expiration time stamp
    /// @param orderAddresses - maker, taker and feeRecipient addresses
    /// @param unsignedOrderValues makerFee, takerFree, price, expirationTimeStamp, and salt (for hashing)
    /// @param orderQty quantity of the order
    /// @param qtyToCancel quantity maker is attempting to cancel
    /// @return qty that was successfully cancelled of order.
    function cancelOrder(
        address[3] orderAddresses,
        uint[5] unsignedOrderValues,
        int orderQty,
        int qtyToCancel
    ) external returns (int qtyCancelled){
        require(qtyToCancel != 0 && qtyToCancel.isSameSign(orderQty));      // cannot cancel 0 and signs must match
        require(!isSettled);
        OrderLib.Order memory order = address(this).createOrder(orderAddresses, unsignedOrderValues, orderQty);
        require(order.maker == msg.sender);                                // only maker can cancel standing order
        if(now >= order.expirationTimeStamp) {
            Error(ErrorCodes.ORDER_EXPIRED, order.orderHash);
            return 0;
        }

        int remainingQty = orderQty.subtract(getQtyFilledOrCancelledFromOrder(order.orderHash));
        if(remainingQty == 0) { // there is no qty remaining to cancel order is dead
            Error(ErrorCodes.ORDER_DEAD, order.orderHash);
            return 0;
        }

        qtyCancelled = MathLib.absMin(qtyToCancel, remainingQty);   // we can only cancel what remains
        cancelledOrderQty[order.orderHash] = cancelledOrderQty[order.orderHash].add(qtyCancelled);
        OrderCancelled(order.maker, order.feeRecipient, qtyCancelled, order.orderHash);
        return qtyCancelled;
    }

    // @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, 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
            addUserNetPosition(userNetPosition, userAddress, qty, price);
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userAddress, userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userAddress, userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                addUserNetPosition(userNetPosition, userAddress, newNetPos, price);
            }
        }
        userNetPosition.netPosition.add(qty);   // keep track of total net pos across all prices for user.
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account and creates the needed Position struct to record the new position.
    /// @param userNetPosition current positions held by user
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(
        UserNetPosition storage userNetPosition,
        address userAddress,
        int qty,
        uint price
    ) private {
        uint neededCollateral = MathLib.calculateNeededCollateral(
            PRICE_FLOOR,
            PRICE_CAP,
            QTY_DECIMAL_PLACES,
            qty,
            price);
        commitCollateralToPool(userAddress, neededCollateral);
        userNetPosition.positions.push(Position(price, qty));   // append array with new position
    }

    /// @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(
                    PRICE_FLOOR,
                    PRICE_CAP,
                    QTY_DECIMAL_PLACES,
                    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(
                    PRICE_FLOOR,
                    PRICE_CAP,
                    QTY_DECIMAL_PLACES,
                    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);
    }

    /// @dev call to oraclize to set up our query and record its hash.
    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;
        }
    }

    /// @dev checks our last query price to see if our contract should enter settlement due to it being past our
    //  expiration date or outside of our tradeable ranges.
    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(lastPrice);
        }
    }

    /// @dev records our final settlement price and fires needed events.
    /// @param finalSettlementPrice final query price at time of settlement
    function settleContract(uint finalSettlementPrice) private {
        settlementPrice = finalSettlementPrice;
        ContractSettled(finalSettlementPrice);
        // TODO: return any remaining ether balance to creator of this contract (no longer needs gas for queries)
    }

    /// @dev over estimates needed gas to power queries until expiration and determines if provided contract
    /// contains enough.
    /// @param secondsToExpiration seconds from now that expiration is scheduled.
    /// @return true if sufficient gas is present to create queries at the designated
    /// frequency from now until expiration
    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/OrderLib.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
//      add function to allow user to pay for an extra query that could trigger settlement
//      require creator to own arbitrary amount of MEM token
//      add in fee functionality needed for nodes and the reduced fees for holders of MEM.

/// @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 OrderLib for address;
    using OrderLib for OrderLib.Order;
    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;
    }

    enum ErrorCodes {
        ORDER_EXPIRED,              // past designated timestamp
        ORDER_DEAD                  // order if fully filled or fully cancelled
    }

    // 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(uint settlePrice);
    event UpdatedUserBalance(address indexed user, uint balance);
    event UpdatedPoolBalance(uint balance);
    event Error(ErrorCodes indexed errorCode, bytes32 indexed orderHash);

    // order events
    event OrderFilled(
        address indexed maker,
        address indexed taker,
        address indexed feeRecipient,
        int filledQty,
        uint paidMakerFee,
        uint paidTakerFee,
        bytes32 orderHash // should this be indexed?
    );
    event OrderCancelled(
        address indexed maker,
        address indexed feeRecipient,
        int cancelledQty,
        bytes32 indexed orderHash
    );

    /// @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);
        // TODO: check for minimum MEM token balance of caller.
        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 orderAddresses - maker, taker and feeRecipient addresses
    /// @param unsignedOrderValues makerFee, takerFree, price, expirationTimeStamp, and salt (for hashing)
    /// @param orderQty quantity of the order
    /// @param qtyToFill quantity taker is willing to fill of original order(max)
    /// @param v order signature
    /// @param r order signature
    /// @param s order signature
    function tradeOrder(
        address[3] orderAddresses,
        uint[5] unsignedOrderValues,
        int orderQty,
        int qtyToFill,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (int filledQty) {
        require(!isSettled);                                // no trading past settlement
        require(orderQty != 0 && qtyToFill != 0);           // no zero trades
        require(orderQty.isSameSign(qtyToFill));            // signs should match
        OrderLib.Order memory order = address(this).createOrder(orderAddresses, unsignedOrderValues, orderQty);

        // taker can be anyone, or specifically the caller!
        require(order.taker == address(0) || order.taker == msg.sender);
        // do not allow self trade
        require(order.maker != address(0) && order.maker != order.taker);
        require(order.maker.isValidSignature(order.orderHash, v, r, s));

        if(now >= order.expirationTimeStamp) {
            Error(ErrorCodes.ORDER_EXPIRED, order.orderHash);
            return 0;
        }

        int remainingQty = orderQty.subtract(getQtyFilledOrCancelledFromOrder(order.orderHash));
        if(remainingQty == 0) { // there is no qty remaining  - cannot fill!
            Error(ErrorCodes.ORDER_DEAD, order.orderHash);
            return 0;
        }

        filledQty = MathLib.absMin(remainingQty, qtyToFill);
        updatePositions(order.maker, order.taker, filledQty, order.price);

        uint paidMakerFee = 0;
        uint paidTakerFee = 0;

        if(order.feeRecipient != address(0)) { // we need to transfer fees to recipient

            if(order.makerFee > 0) {

            }

            if(order.takerFee > 0) {

            }
        }

        OrderFilled(
            order.maker,
            order.taker,
            order.feeRecipient,
            filledQty,
            paidMakerFee,
            paidTakerFee,
            order.orderHash
        );

        return filledQty;
    }

    /// @notice called by the maker of an order to attempt to cancel the order before its expiration time stamp
    /// @param orderAddresses - maker, taker and feeRecipient addresses
    /// @param unsignedOrderValues makerFee, takerFree, price, expirationTimeStamp, and salt (for hashing)
    /// @param orderQty quantity of the order
    /// @param qtyToCancel quantity maker is attempting to cancel
    /// @return qty that was successfully cancelled of order.
    function cancelOrder(
        address[3] orderAddresses,
        uint[5] unsignedOrderValues,
        int orderQty,
        int qtyToCancel
    ) external returns (int qtyCancelled){
        require(qtyToCancel != 0 && qtyToCancel.isSameSign(orderQty));      // cannot cancel 0 and signs must match
        require(!isSettled);
        OrderLib.Order memory order = address(this).createOrder(orderAddresses, unsignedOrderValues, orderQty);
        require(order.maker == msg.sender);                                // only maker can cancel standing order
        if(now >= order.expirationTimeStamp) {
            Error(ErrorCodes.ORDER_EXPIRED, order.orderHash);
            return 0;
        }

        int remainingQty = orderQty.subtract(getQtyFilledOrCancelledFromOrder(order.orderHash));
        if(remainingQty == 0) { // there is no qty remaining to cancel order is dead
            Error(ErrorCodes.ORDER_DEAD, order.orderHash);
            return 0;
        }

        qtyCancelled = MathLib.absMin(qtyToCancel, remainingQty);   // we can only cancel what remains
        cancelledOrderQty[order.orderHash] = cancelledOrderQty[order.orderHash].add(qtyCancelled);
        OrderCancelled(order.maker, order.feeRecipient, qtyCancelled, order.orderHash);
        return qtyCancelled;
    }

    // @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, 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
            addUserNetPosition(userNetPosition, userAddress, qty, price);
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userAddress, userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userAddress, userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                addUserNetPosition(userNetPosition, userAddress, newNetPos, price);
            }
        }
        userNetPosition.netPosition.add(qty);   // keep track of total net pos across all prices for user.
    }

    /// @dev calculates the needed collateral for a new position and commits it to the pool removing it from the
    /// users account and creates the needed Position struct to record the new position.
    /// @param userNetPosition current positions held by user
    /// @param userAddress address of user entering into the position
    /// @param qty signed quantity of the trade
    /// @param price agreed price of trade
    function addUserNetPosition(
        UserNetPosition storage userNetPosition,
        address userAddress,
        int qty,
        uint price
    ) private {
        uint neededCollateral = MathLib.calculateNeededCollateral(
            PRICE_FLOOR,
            PRICE_CAP,
            QTY_DECIMAL_PLACES,
            qty,
            price);
        commitCollateralToPool(userAddress, neededCollateral);
        userNetPosition.positions.push(Position(price, qty));   // append array with new position
    }

    /// @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(
                    PRICE_FLOOR,
                    PRICE_CAP,
                    QTY_DECIMAL_PLACES,
                    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(
                    PRICE_FLOOR,
                    PRICE_CAP,
                    QTY_DECIMAL_PLACES,
                    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);
    }

    /// @dev call to oraclize to set up our query and record its hash.
    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;
        }
    }

    /// @dev checks our last query price to see if our contract should enter settlement due to it being past our
    //  expiration date or outside of our tradeable ranges.
    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(lastPrice);
        }
    }

    /// @dev records our final settlement price and fires needed events.
    /// @param finalSettlementPrice final query price at time of settlement
    function settleContract(uint finalSettlementPrice) private {
        settlementPrice = finalSettlementPrice;
        ContractSettled(finalSettlementPrice);
        // TODO: return any remaining ether balance to creator of this contract (no longer needs gas for queries)
    }

    /// @dev over estimates needed gas to power queries until expiration and determines if provided contract
    /// contains enough.
    /// @param secondsToExpiration seconds from now that expiration is scheduled.
    /// @return true if sufficient gas is present to create queries at the designated
    /// frequency from now until expiration
    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);
    }
}


Notification of trading volume added

Notification of trading volume added

contract PlutusDex {
    enum CurrencySymbol { EUR, GBP, USD }
    //Addresses of ethereum accounts that are approved to verify bitcoin transactions,
    //, verify/perform deposit of fiat to escrow account and verify/perform deposit
    //of fiat to vdc
    mapping(address => bool) approvedPlutusCentral;

    struct FiatDeposit {
        address trader;
        uint fiatDeposited;
        CurrencySymbol fiatSymbol;
        uint btcAsked;
        //From https://forum.ethereum.org/discussion/3139/store-bitcoin-address-in-smart-contract
        bytes20 btcAddress; //Public address to deposit the bitcoin too
    }

    mapping(address => FiatDeposit) fiatDeposits;

    uint btcTradingVolume;

    event FiatDeposited(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress);

    event VdcLoaded(bytes32 userVdcIban, uint fiatAmount, CurrencySymbol fiatSymbol, uint btcTradingVolume);

    function PlutusDex() {
        approvedPlutusCentral[msg.sender] = true;
        btcTradingVolume = 0;
    }

    function depositFiat(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved fiat trackers can make deposit
            throw;
        }
        fiatDeposits[trader] = FiatDeposit(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
        //Emit event, to notify all plutus-users
        FiatDeposited(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
    }

    function offerBtc(address trader, uint btcOffered, bytes32 userVdcIban) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved btc trackers can offer bitcoin
            throw;
        }
        //TODO some Null check
        FiatDeposit deposited = fiatDeposits[trader];
        uint btcTraded;
        if (deposited.btcAsked < btcOffered) {
            btcTraded = btcOffered;
        } else {
            btcTraded = deposited.btcAsked;
        }
        uint fiatReceived = deposited.fiatDeposited * (btcTraded / btcOffered);
        deposited.btcAsked -= btcTraded;
        deposited.fiatDeposited -= fiatReceived;
        btcTradingVolume += btcTraded;
        //Notify user
        VdcLoaded(userVdcIban, fiatReceived, deposited.fiatSymbol, btcTradingVolume);
    }
}

contract PlutusDex {
    enum CurrencySymbol { EUR, GBP, USD }
    //Addresses of ethereum accounts that are approved to verify bitcoin transactions,
    //, verify/perform deposit of fiat to escrow account and verify/perform deposit
    //of fiat to vdc
    mapping(address => bool) approvedPlutusCentral;

    struct FiatDeposit {
        address trader;
        uint fiatDeposited;
        CurrencySymbol fiatSymbol;
        uint btcAsked;
        //From https://forum.ethereum.org/discussion/3139/store-bitcoin-address-in-smart-contract
        bytes20 btcAddress; //Public address to deposit the bitcoin too
    }

    mapping(address => FiatDeposit) fiatDeposits;

    uint btcTradingVolume;

    event FiatDeposited(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress);

    event VdcLoaded(bytes32 userVdcIban, uint fiatAmount, CurrencySymbol fiatSymbol);

    function PlutusDex() {
        approvedPlutusCentral[msg.sender] = true;
        btcTradingVolume = 0;
    }

    function depositFiat(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved fiat trackers can make deposit
            throw;
        }
        fiatDeposits[trader] = FiatDeposit(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
        //Emit event, to notify all plutus-users
        FiatDeposited(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
    }

    function offerBtc(address trader, uint btcOffered, bytes32 userVdcIban) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved btc trackers can offer bitcoin
            throw;
        }
        //TODO some Null check
        FiatDeposit deposited = fiatDeposits[trader];
        uint btcTraded;
        if (deposited.btcAsked < btcOffered) {
            btcTraded = btcOffered;
        } else {
            btcTraded = deposited.btcAsked;
        }
        uint fiatReceived = deposited.fiatDeposited * (btcTraded / btcOffered);
        deposited.btcAsked -= btcTraded;
        deposited.fiatDeposited -= fiatReceived;
        btcTradingVolume += btcTraded;
        //Notify user
        VdcLoaded(userVdcIban, fiatReceived, deposited.fiatSymbol);
    }
}


Fix doxygen error.

Fix doxygen error.

/**
 * @title Glofile
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract Glofile {

  enum GlofileType { Anon, Person, Project, Organization, Proxy, Parody, Bot }
  enum SafetyLevel { Safe, NSFW, NSFL }

  struct Glofile {
    bool dontIndex;
    GlofileType glofileType;
    SafetyLevel safetyLevel;
    string fullName;
    string location;
    bytes topics;
    bytes uris;
    bytes parents;
    bytes children;
    bytes3[] foregroundColors;
    bytes3[] backgroundColors;
    bytes3[] bioLangs;
    bytes[] avatars;
    bytes[] coverImages;
    bytes[] backgroundImages;
    bytes[] publicKeys;
    mapping (bytes3 => bytes) bioTranslations;
  }

  mapping (address => Glofile) glofiles;

  event Updated(address indexed account);
  event Deleted(address indexed account);
  event PublicKeyAdded(address indexed account, uint indexed i);
  event PublicKeyDeleted(address indexed account, uint indexed i);

  /**
   * @notice Set your Glofile don't index flag to `dontIndex`
   * @dev Sets the "don't index" flag. Glofiles with this flag set should only be accessible directly and not be discoverable via search.
   * @param dontIndex flag to indicate that this Glofile should not be indexed
   */
  function setDontIndex(bool dontIndex) {
    glofiles[msg.sender].dontIndex = dontIndex;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile type to `glofileType`
   * @dev Sets the Glofile type.
   * @param glofileType Glofile type
   */
  function setGlofileType(GlofileType glofileType) {
    glofiles[msg.sender].glofileType = glofileType;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile safety level to `safetyLevel`
   * @dev Sets the safety level. This applies to the Glofile itself. The account may publish other content that needs to be rated separately.
   * @param safetyLevel safety level
   */
  function setSafetyLevel(SafetyLevel safetyLevel) {
    glofiles[msg.sender].safetyLevel = safetyLevel;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile full name to `fullName`
   * @dev Sets the full name.
   * @param fullName UTF-8 string of full name - max length 128 chars
   */
  function setFullName(string fullName) {
    glofiles[msg.sender].fullName = fullName;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile location
   * @dev Sets the location. A dedicated contract could be used for more sophisticated location functionality.
   * @param location UTF-8 string of location - max string length 128 chars
   */
  function setLocation(string location) {
    glofiles[msg.sender].location = location;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile topics
   * @dev Sets the topics. This is a hint that this account will be publishing content in other contracts under these topics.
   * @param topics raw Deflated UTF-8 string of space-separated topics
   */
  function setTopics(bytes topics) {
    glofiles[msg.sender].topics = topics;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile URIs
   * @dev Sets the URIs. These URIs can reference anthing associated with the account, e.g. social network accounts.
   * @param uris raw Deflated UTF-8 string of space-separated percent-encoded URIs
   */
  function setUris(bytes uris) {
    glofiles[msg.sender].uris = uris;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile parents
   * @dev Sets the parents.
   * @param parents raw Deflated UTF-8 string of space-separated usernames
   */
  function setParents(bytes parents) {
    glofiles[msg.sender].parents = parents;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile children
   * @dev Sets the children.
   * @param children Raw Deflated UTF-8 string of space-separated usernames
   */
  function setChildren(bytes children) {
    glofiles[msg.sender].children = children;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile foreground colors
   * @dev Sets all the foreground colors.
   * @param colors array of RGB triplets
   */
  function setForegroundColors(bytes3[] colors) {
    glofiles[msg.sender].foregroundColors = colors;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile background colors
   * @dev Sets all the background colors.
   * @param colors array of RGB triplets
   */
  function setBackgroundColors(bytes3[] colors) {
    glofiles[msg.sender].backgroundColors = colors;
    Updated(msg.sender);
  }

  /**
   * @notice Get Glofile basic info
   * @dev Gets all the info that can be retreived in a single call.
   * @param address account to access - defaults to sender
   * @return dontIndex flag to indicate that this Glofile should not be indexed
   * @return glofileType Glofile type
   * @return safetyLevel safety level
   * @return fullName UTF-8 string of full name
   * @return location UTF-8 string of location
   * @return topics raw Deflated UTF-8 of space-separated topics
   * @return uris raw Deflated UTF-8 string of space-separated percent-encoded URIs.
   * @return parents raw Deflated UTF-8 string of space-separated usernames
   * @return children raw Deflated UTF-8 string of space-separated usernames
   * @return foregroundColors array of RGB triplets of foreground colors
   * @return backgroundColors array of RGB triplets of background colors
   */
  function getBasicInfo(address account) constant returns (bool dontIndex, GlofileType glofileType, SafetyLevel safetyLevel, bytes fullName, bytes location, bytes topics, bytes uris, bytes parents, bytes children, bytes3[] foregroundColors, bytes3[] backgroundColors) {
    if (account == 0) account = msg.sender;
    Glofile glofile = glofiles[account];
    dontIndex = glofile.dontIndex;
    glofileType = glofile.glofileType;
    safetyLevel = glofile.safetyLevel;
    fullName = glofile.fullName;
    location = glofile.location;
    topics = glofile.topics;
    uris = glofile.uris;
    parents = glofile.parents;
    children = glofile.children;
    foregroundColors = glofile.foregroundColors;
    backgroundColors = glofile.backgroundColors;
  }

  /**
   * @notice Set your Glofile bio with langauge code `lang`
   * @dev Sets the bio in a specific language.
   * @param lang 3 letter ISO 639-3 language code
   * @param translation raw Deflated UTF-8 Markdown of bio - max Markdown length 256 chars
   */
  function setBio(bytes3 lang, bytes translation) {
    Glofile glofile = glofiles[msg.sender];
    // Check if we already have the language code.
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      if (glofile.bioLangs[i] == lang) {
        break;
      }
    }
    if (i == glofile.bioLangs.length) {
      // We didn't find it. Try to find a free slot.
      for (i = 0; i < glofile.bioLangs.length; i++) {
        if (glofile.bioLangs[i] == 0) {
          break;
        }
      }
      if (i == glofile.bioLangs.length) {
        // We didn't find a free slot. Make the array bigger.
        glofile.bioLangs.length++;
      }
      glofile.bioLangs[i] = lang;
    }
    // Set translation.
    glofile.bioTranslations[lang] = translation;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile bio with language code `lang`
   * @dev Deletes a bio translation.
   * @param lang 3 letter ISO 639-3 language code
   */
  function deleteBio(bytes3 lang) {
    Glofile glofile = glofiles[msg.sender];
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      if (glofile.bioLangs[i] == lang) {
        delete glofile.bioLangs[i];
        break;
      }
    }
    // Delete the actual mapping in case a client accesses without checking
    // language key.
    delete glofile.bioTranslations[lang];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all of your Glofile bio translations
   * @dev Deletes all of the bio translations.
   */
  function deleteAllBioTranslations() {
    Glofile glofile = glofiles[msg.sender];
    // Delete the actual mappings in case a client accesses without checking
    // language key.
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      delete glofile.bioTranslations[glofile.bioLangs[i]];
    }
    delete glofile.bioLangs;
    Updated(msg.sender);
  }

  /**
   * @notice Get the list of language code a Glofile bio is available in
   * @dev Gets the list of language codes the bio is available in.
   * @param address account to access - defaults to sender
   * @return array of 3 letter ISO 639-3 language codes
   */
  function getBioLangCodes(address account) constant returns (bytes3[]) {
    if (account == 0) account = msg.sender;
    return glofiles[account].bioLangs;
  }

  /**
   * @notice Get the Glofile bio with language code `lang`
   * @dev Gets the bio in a specific language.
   * @param lang 3 letter ISO 639-3 language code
   * @param address account to access - defaults to sender
   * @return raw Deflated UTF-8 Markdown of bio
   */
  function getBioTranslation(bytes3 lang, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].bioTranslations[lang];
  }

  /**
   * @notice Set your Glofile avatar with index `i` to `ipfsHash`
   * @dev Sets the avatar with a specific index to an IPFS hash.
   * @param i index of avatar to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setAvatar(uint i, bytes ipfsHash) {
    bytes[] avatars = glofiles[msg.sender].avatars;
    // Make sure the array is long enough.
    if (avatars.length <= i) {
      avatars.length = i;
    }
    avatars[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile avatar with index `i`
   * @dev Deletes an avatar with a specific index.
   * @param i index of avatar to delete
   */
  function deleteAvatar(uint i) {
    delete glofiles[msg.sender].avatars[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile avatars
   * @dev Deletes all avatars from the Glofile.
   */
  function deleteAllAvatars() {
    delete glofiles[msg.sender].avatars;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile avatars
   * @dev Gets the number of avatars.
   * @param address account to access - defaults to sender
   * @return number of avatars
   */
  function getAvatarCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].avatars.length;
  }

  /**
   * @notice Get the Glofile avatar with index `i`
   * @dev Gets the avatar with a specific index.
   * @param i index of avatar to get
   * @param address account to access - defaults to sender
   * @return binary IPFS hash of image
   */
  function getAvatar(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].avatars[i];
  }

  /**
   * @notice Set your Glofile cover image with index `i` to `ipfsHash`
   * @dev Sets the cover image with a specific index to an IPFS hash.
   * @param i index of cover image to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setCoverImage(uint i, bytes ipfsHash) {
    bytes[] coverImages = glofiles[msg.sender].coverImages;
    // Make sure the array is long enough.
    if (coverImages.length <= i) {
      coverImages.length = i;
    }
    coverImages[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile cover image with index `i`
   * @dev Deletes the cover image with a specific index.
   * @param i index of cover image to delete
   */
  function deleteCoverImage(uint i) {
    delete glofiles[msg.sender].coverImages[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile cover images
   * @dev Deletes all cover images from the Glofile.
   */
  function deleteAllCoverImages() {
    delete glofiles[msg.sender].coverImages;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile cover images
   * @dev Gets the number of cover images.
   * @param address account to access - defaults to sender
   * @return number of cover images
   */
  function getCoverImageCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].coverImages.length;
  }

  /**
   * @notice Get the Glofile cover image with index `i`
   * @dev Gets the cover image with a specific index.
   * @param i index of cover image to get
   * @param ipfsHash binary ipfs hash of image
   * @return binary IPFS hash of image
   */
  function getCoverImage(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].coverImages[i];
  }

  /**
   * @notice Set your Glofile background image with index `i` to `ipfsHash`
   * @dev Sets the background image with a specific index to an IPFS hash.
   * @param i index of background image to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setBackgroundImage(uint i, bytes ipfsHash) {
    bytes[] backgroundImages = glofiles[msg.sender].backgroundImages;
    // Make sure the array is long enough.
    if (backgroundImages.length <= i) {
      backgroundImages.length = i;
    }
    backgroundImages[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile background image with index `i`
   * @dev Deletes the background image with a specific index.
   * @param i index of background image to delete
   */
  function deleteBackgroundImage(uint i) {
    delete glofiles[msg.sender].backgroundImages[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile background images
   * @dev Deletes all the background images.
   */
  function deleteAllBackgroundImages() {
    delete glofiles[msg.sender].backgroundImages;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile background images
   * @dev Gets the number of background images.
   * @param address account to access - defaults to sender
   * @return number of background images
   */
  function getBackgroundImageCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].backgroundImages.length;
  }

  /**
   * @notice Get the Glofile background image with index `i`
   * @dev Gets the background image with a specific index.
   * @param i index of cover image to get
   * @param address account to access - defaults to sender
   * @return binary IPFS hash of image
   */
  function getBackgroundImage(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].backgroundImages[i];
  }

  /**
   * @notice Add a public key to your Glofile
   * @dev Adds a public key.
   * @param publicKey Raw Deflated UTF-8 public key
   */
  function addPublicKey(bytes publicKey) {
    Glofile glofile = glofiles[msg.sender];
    uint i = glofile.publicKeys.length++;
    glofile.publicKeys[i] = publicKey;
    PublicKeyAdded(msg.sender, i);
  }

  /**
   * @notice Delete the public key with index `i` from your Glofile
   * @dev Deletes the public key with a specific index.
   * @param i index of public key to delete
   */
  function deletePublicKey(uint i) {
    delete glofiles[msg.sender].publicKeys[i];
    PublicKeyDeleted(msg.sender, i);
  }

  /**
   * @notice Get the number of public keys in the Glofile
   * @dev Gets the number of public keys.
   * @param address account to access - defaults to sender
   * @return the number of public keys
   */
  function getPublicKeyCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].publicKeys.length;
  }

  /**
   * @notice Get the public key with index `i` from the Glofile
   * @dev Gets the public key with a specific index.
   * @param i index of public key to get
   * @param address account to access - defaults to sender
   * @return raw Deflated UTF-8 public key
   */
  function getPublicKey(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].publicKeys[i];
  }

  /**
   * @notice Copy entire Glofile from `source`
   * @dev Copies the Glofile from another account to this one.
   * @param source account of Glofile to copy from
   * TODO: check this works
   */
  function copyEntireGlofile(address source) {
    glofiles[msg.sender] = glofiles[source];
    Updated(msg.sender);
  }

  /**
   * @notice Delete your entire Glofile
   * @dev Deletes the Glofile completely.
   */
  function deleteEntireGlofile() {
    delete glofiles[msg.sender];
    Deleted(msg.sender);
  }

}

/**
 * @title Glofile
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract Glofile {

  enum GlofileType { Anon, Person, Project, Organization, Proxy, Parody, Bot }
  enum SafetyLevel { Safe, NSFW, NSFL }

  struct Glofile {
    bool dontIndex;
    GlofileType glofileType;
    SafetyLevel safetyLevel;
    string fullName;
    string location;
    bytes topics;
    bytes uris;
    bytes parents;
    bytes children;
    bytes3[] foregroundColors;
    bytes3[] backgroundColors;
    bytes3[] bioLangs;
    bytes[] avatars;
    bytes[] coverImages;
    bytes[] backgroundImages;
    bytes[] publicKeys;
    mapping (bytes3 => bytes) bioTranslations;
  }

  mapping (address => Glofile) glofiles;

  event Updated(address indexed account);
  event Deleted(address indexed account);
  event PublicKeyAdded(address indexed account, uint indexed i);
  event PublicKeyDeleted(address indexed account, uint indexed i);

  /**
   * @notice Set your Glofile don't index flag to `dontIndex`
   * @dev Sets the "don't index" flag. Glofiles with this flag set should only be accessible directly and not be discoverable via search.
   * @param dontIndex flag to indicate that this Glofile should not be indexed
   */
  function setDontIndex(bool dontIndex) {
    glofiles[msg.sender].dontIndex = dontIndex;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile type to `glofileType`
   * @dev Sets the Glofile type.
   * @param glofileType Glofile type
   */
  function setGlofileType(GlofileType glofileType) {
    glofiles[msg.sender].glofileType = glofileType;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile safety level to `safetyLevel`
   * @dev Sets the safety level. This applies to the Glofile itself. The account may publish other content that needs to be rated separately.
   * @param safetyLevel safety level
   */
  function setSafetyLevel(SafetyLevel safetyLevel) {
    glofiles[msg.sender].safetyLevel = safetyLevel;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile full name to `fullName`
   * @dev Sets the full name.
   * @param fullName UTF-8 string of full name - max length 128 chars
   */
  function setFullName(string fullName) {
    glofiles[msg.sender].fullName = fullName;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile location
   * @dev Sets the location. A dedicated contract could be used for more sophisticated location functionality.
   * @param location UTF-8 string of location - max string length 128 chars
   */
  function setLocation(string location) {
    glofiles[msg.sender].location = location;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile topics
   * @dev Sets the topics. This is a hint that this account will be publishing content in other contracts under these topics.
   * @param topics raw Deflated UTF-8 string of space-separated topics
   */
  function setTopics(bytes topics) {
    glofiles[msg.sender].topics = topics;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile URIs
   * @dev Sets the URIs. These URIs can reference anthing associated with the account, e.g. social network accounts.
   * @param uris raw Deflated UTF-8 string of space-separated percent-encoded URIs
   */
  function setUris(bytes uris) {
    glofiles[msg.sender].uris = uris;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile parents
   * @dev Sets the parents.
   * @param parents raw Deflated UTF-8 string of space-separated usernames
   */
  function setParents(bytes parents) {
    glofiles[msg.sender].parents = parents;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile children
   * @dev Sets the children.
   * @param children Raw Deflated UTF-8 string of space-separated usernames
   */
  function setChildren(bytes children) {
    glofiles[msg.sender].children = children;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile foreground colors
   * @dev Sets all the foreground colors.
   * @param colors array of RGB triplets
   */
  function setForegroundColors(bytes3[] colors) {
    glofiles[msg.sender].foregroundColors = colors;
    Updated(msg.sender);
  }

  /**
   * @notice Set your Glofile background colors
   * @dev Sets all the background colors.
   * @param colors array of RGB triplets
   */
  function setBackgroundColors(bytes3[] colors) {
    glofiles[msg.sender].backgroundColors = colors;
    Updated(msg.sender);
  }

  /**
   * @notice Get Glofile basic info
   * @dev Gets all the info that can be retreived in a single call.
   * @param address account to access - defaults to sender
   * @return dontIndex flag to indicate that this Glofile should not be indexed
   * @return glofileType Glofile type
   * @return safetyLevel safety level
   * @return fullName UTF-8 string of full name
   * @return location UTF-8 string of location
   * @return topics raw Deflated UTF-8 of space-separated topics
   * @return uris raw Deflated UTF-8 string of space-separated percent-encoded URIs.
   * @return parents raw Deflated UTF-8 string of space-separated usernames
   * @return children raw Deflated UTF-8 string of space-separated usernames
   * @return foregroundColors array of RGB triplets of foreground colors
   * @return backgroundColors array of RGB triplets of background colors
   */
  function getBasicInfo(address account) constant returns (bool dontIndex, GlofileType glofileType, SafetyLevel safetyLevel, bytes fullName, bytes location, bytes topics, bytes uris, bytes parents, bytes children, bytes3[] foregroundColors, bytes3[] backgroundColors) {
    if (account == 0) account = msg.sender;
    Glofile glofile = glofiles[account];
    dontIndex = glofile.dontIndex;
    glofileType = glofile.glofileType;
    safetyLevel = glofile.safetyLevel;
    fullName = glofile.fullName;
    location = glofile.location;
    topics = glofile.topics;
    uris = glofile.uris;
    parents = glofile.parents;
    children = glofile.children;
    foregroundColors = glofile.foregroundColors;
    backgroundColors = glofile.backgroundColors;
  }

  /**
   * @notice Set your Glofile bio with langauge code `lang`
   * @dev Sets the bio in a specific language.
   * @param lang 3 letter ISO 639-3 language code
   * @param translation raw Deflated UTF-8 Markdown of bio - max Markdown length 256 chars
   */
  function setBio(bytes3 lang, bytes translation) {
    Glofile glofile = glofiles[msg.sender];
    // Check if we already have the language code.
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      if (glofile.bioLangs[i] == lang) {
        break;
      }
    }
    if (i == glofile.bioLangs.length) {
      // We didn't find it. Try to find a free slot.
      for (i = 0; i < glofile.bioLangs.length; i++) {
        if (glofile.bioLangs[i] == 0) {
          break;
        }
      }
      if (i == glofile.bioLangs.length) {
        // We didn't find a free slot. Make the array bigger.
        glofile.bioLangs.length++;
      }
      glofile.bioLangs[i] = lang;
    }
    // Set translation.
    glofile.bioTranslations[lang] = translation;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile bio with language code `lang`
   * @dev Deletes a bio translation.
   * @param lang 3 letter ISO 639-3 language code
   */
  function deleteBio(bytes3 lang) {
    Glofile glofile = glofiles[msg.sender];
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      if (glofile.bioLangs[i] == lang) {
        delete glofile.bioLangs[i];
        break;
      }
    }
    // Delete the actual mapping in case a client accesses without checking
    // language key.
    delete glofile.bioTranslations[lang];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all of your Glofile bio translations
   * @dev Deletes all of the bio translations.
   */
  function deleteAllBioTranslations() {
    Glofile glofile = glofiles[msg.sender];
    // Delete the actual mappings in case a client accesses without checking
    // language key.
    for (uint i = 0; i < glofile.bioLangs.length; i++) {
      delete glofile.bioTranslations[glofile.bioLangs[i]];
    }
    delete glofile.bioLangs;
    Updated(msg.sender);
  }

  /**
   * @notice Get the list of language code a Glofile bio is available in
   * @dev Gets the list of language codes the bio is available in.
   * @param address account to access - defaults to sender
   * @return array of 3 letter ISO 639-3 language codes
   */
  function getBioLangCodes(address account) constant returns (bytes3[]) {
    if (account == 0) account = msg.sender;
    return glofiles[account].bioLangs;
  }

  /**
   * @notice Get the Glofile bio with language code `lang`
   * @dev Gets the bio in a specific language.
   * @param lang 3 letter ISO 639-3 language code
   * @param address account to access - defaults to sender
   * @return raw Deflated UTF-8 Markdown of bio
   */
  function getBioTranslation(bytes3 lang, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].bioTranslations[lang];
  }

  /**
   * @notice Set your Glofile avatar with index `i` to `ipfsHash`
   * @dev Sets the avatar with a specific index to an IPFS hash.
   * @param i index of avatar to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setAvatar(uint i, bytes ipfsHash) {
    bytes[] avatars = glofiles[msg.sender].avatars;
    // Make sure the array is long enough.
    if (avatars.length <= i) {
      avatars.length = i;
    }
    avatars[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile avatar with index `i`
   * @dev Deletes an avatar with a specific index.
   * @param i index of avatar to delete
   */
  function deleteAvatar(uint i) {
    delete glofiles[msg.sender].avatars[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile avatars
   * @dev Deletes all avatars from the Glofile.
   */
  function deleteAllAvatars() {
    delete glofiles[msg.sender].avatars;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile avatars
   * @dev Gets the number of avatars.
   * @param address account to access - defaults to sender
   * @return number of avatars
   */
  function getAvatarCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].avatars.length;
  }

  /**
   * @notice Get the Glofile avatar with index `i`
   * @dev Gets the avatar with a specific index.
   * @param i index of avatar to get
   * @param address account to access - defaults to sender
   * @return binary IPFS hash of image
   */
  function getAvatar(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].avatars[i];
  }

  /**
   * @notice Set your Glofile cover image with index `i` to `ipfsHash`
   * @dev Sets the cover image with a specific index to an IPFS hash.
   * @param i index of cover image to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setCoverImage(uint i, bytes ipfsHash) {
    bytes[] coverImages = glofiles[msg.sender].coverImages;
    // Make sure the array is long enough.
    if (coverImages.length <= i) {
      coverImages.length = i;
    }
    coverImages[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile cover image with index `i`
   * @dev Deletes the cover image with a specific index.
   * @param i index of cover image to delete
   */
  function deleteCoverImage(uint i) {
    delete glofiles[msg.sender].coverImages[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile cover images
   * @dev Deletes all cover images from the Glofile.
   */
  function deleteAllCoverImages() {
    delete glofiles[msg.sender].coverImages;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile cover images
   * @dev Gets the number of cover images.
   * @param address account to access - defaults to sender
   * @return number of cover images
   */
  function getCoverImageCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].coverImages.length;
  }

  /**
   * @notice Get the Glofile cover image with index `i`
   * @dev Gets the cover image with a specific index.
   * @param i index of cover image to get
   * @param ipfsHash binary ipfs hash of image
   * @return binary IPFS hash of image
   */
  function getCoverImage(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].coverImages[i];
  }

  /**
   * @notice Set your Glofile background image with index `i` to `ipfsHash`
   * @dev Sets the background image with a specific index to an IPFS hash.
   * @param i index of background image to set
   * @param ipfsHash binary IPFS hash of image
   */
  function setBackgroundImage(uint i, bytes ipfsHash) {
    bytes[] backgroundImages = glofiles[msg.sender].backgroundImages;
    // Make sure the array is long enough.
    if (backgroundImages.length <= i) {
      backgroundImages.length = i;
    }
    backgroundImages[i] = ipfsHash;
    Updated(msg.sender);
  }

  /**
   * @notice Delete your Glofile background image with index `i`
   * @dev Deletes the background image with a specific index.
   * @param i index of background image to get
   */
  function deleteBackgroundImage(uint i) {
    delete glofiles[msg.sender].backgroundImages[i];
    Updated(msg.sender);
  }

  /**
   * @notice Delete all your Glofile background images
   * @dev Deletes all the background images.
   */
  function deleteAllBackgroundImages() {
    delete glofiles[msg.sender].backgroundImages;
    Updated(msg.sender);
  }

  /**
   * @notice Get the number of Glofile background images
   * @dev Gets the number of background images.
   * @param address account to access - defaults to sender
   * @return number of background images
   */
  function getBackgroundImageCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].backgroundImages.length;
  }

  /**
   * @notice Get the Glofile background image with index `i`
   * @dev Gets the background image with a specific index.
   * @param i index of cover image to get
   * @param address account to access - defaults to sender
   * @return binary IPFS hash of image
   */
  function getBackgroundImage(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].backgroundImages[i];
  }

  /**
   * @notice Add a public key to your Glofile
   * @dev Adds a public key.
   * @param publicKey Raw Deflated UTF-8 public key
   */
  function addPublicKey(bytes publicKey) {
    Glofile glofile = glofiles[msg.sender];
    uint i = glofile.publicKeys.length++;
    glofile.publicKeys[i] = publicKey;
    PublicKeyAdded(msg.sender, i);
  }

  /**
   * @notice Delete the public key with index `i` from your Glofile
   * @dev Deletes the public key with a specific index.
   * @param i index of public key to delete
   */
  function deletePublicKey(uint i) {
    delete glofiles[msg.sender].publicKeys[i];
    PublicKeyDeleted(msg.sender, i);
  }

  /**
   * @notice Get the number of public keys in the Glofile
   * @dev Gets the number of public keys.
   * @param address account to access - defaults to sender
   * @return the number of public keys
   */
  function getPublicKeyCount(address account) constant returns (uint) {
    if (account == 0) account = msg.sender;
    return glofiles[account].publicKeys.length;
  }

  /**
   * @notice Get the public key with index `i` from the Glofile
   * @dev Gets the public key with a specific index.
   * @param i index of public key to get
   * @param address account to access - defaults to sender
   * @return raw Deflated UTF-8 public key
   */
  function getPublicKey(uint i, address account) constant returns (bytes) {
    if (account == 0) account = msg.sender;
    return glofiles[account].publicKeys[i];
  }

  /**
   * @notice Copy entire Glofile from `source`
   * @dev Copies the Glofile from another account to this one.
   * @param source account of Glofile to copy from
   * TODO: check this works
   */
  function copyEntireGlofile(address source) {
    glofiles[msg.sender] = glofiles[source];
    Updated(msg.sender);
  }

  /**
   * @notice Delete your entire Glofile
   * @dev Deletes the Glofile completely.
   */
  function deleteEntireGlofile() {
    delete glofiles[msg.sender];
    Deleted(msg.sender);
  }

}


Adjust spec again

Adjust spec again

pragma solidity ^0.4.21;

contract NewPragma {
  uint x;

  constructor() public {
    x = 5;
  }
}

pragma solidity ^0.4.21;

contract NewPragma {
  uint x;

  function NewPragma() public {
    x = 5;
  }
}


Change createPresaleInvestment to use SafeMath.

pragma solidity ^0.4.10;
import './SafeMath.sol';

contract TokenSyndicate {
    uint256 public constant kwei = 1000;

    address public tokenContract;
    uint256 public tokenExchangeRate;
    uint256 public minBountyPerKwei;
    uint256 public maxPresaleWeiAllowed;
    uint256 public refundStartBlock;
    address public winner;

    uint256 public totalPresale;
    uint256 public totalBounties;

    mapping(address => uint256) public presaleBalances;
    mapping(address => uint256) public bountyBalances;

    event LogCreatePresaleInvestment(address indexed _to, uint256 bounty, uint256 presale);
    event LogRefundPresaleInvestment(address indexed _to, uint256 bounty, uint256 presale);
    event LogWithdrawTokens(address indexed _to, uint256 tokens);
    event LogWithdrawBounty(address indexed _to, uint256 bounty);

    function TokenSyndicate(
    address _tokenContract,
    uint256 _tokenExchangeRate,
    uint256 _minBountyPerKwei,
    uint256 _maxPresaleWeiAllowed,
    uint256 _refundStartBlock){
        assert(_minBountyPerKwei < kwei);       // do not allow a bounty of 100%.
        assert(_minBountyPerKwei > 0);          // do not allow a bounty of zero.
        assert(_maxPresaleWeiAllowed > 0);      // the eth allowed must be greater than zero.
        assert(_tokenContract != address(0));   // the token contract may not be at the zero address.
        assert(_tokenExchangeRate > 0);         // the token exchange rate must not be zero.
        assert(_refundStartBlock >= block.number);   // the refund start time must be now or later

        tokenContract = _tokenContract;
        tokenExchangeRate = _tokenExchangeRate;
        minBountyPerKwei = _minBountyPerKwei;
        maxPresaleWeiAllowed = _maxPresaleWeiAllowed;
        refundStartBlock = _refundStartBlock;

        totalPresale = 0;
        totalBounties = 0;
        winner = address(0);
    }

    /*
        Invest in this contract in order to have tokens purchased on your behalf when the buyTokens() contract
        is called without a `throw`.
    */
    function createPresaleInvestment(uint256 _bountyPerKwei) payable external {
        assert(_bountyPerKwei >= minBountyPerKwei);
        assert(msg.value > 0);

        /*
            As the EVM does not currently support decimals, we are multiplying both msg.value and the bounty
            by 1000 before calculating the bounty ratio in order to gain a reasonable degree of precision.
        */
        uint256 msg_value_with_precision = SafeMath.mul(msg.value, kwei);
        uint256 bounty_with_precision = SafeMath.mul(_bountyPerKwei, SafeMath.div(msg_value_with_precision, kwei));
        uint256 final_bounty = SafeMath.div(bounty_with_precision, kwei);
        uint256 final_presale = SafeMath.sub(msg.value, final_bounty);

        bountyBalances[msg.sender] = SafeMath.add(bountyBalances[msg.sender], final_bounty);
        presaleBalances[msg.sender] = SafeMath.add(presaleBalances[msg.sender], final_presale);
        totalBounties = SafeMath.add(totalBounties, final_bounty);
        totalPresale = SafeMath.add(totalPresale, final_presale);
        assert(totalPresale <= maxPresaleWeiAllowed);
        LogCreatePresaleInvestment(msg.sender, final_bounty, final_presale);       // create an event
    }

    /*
        Get the presaleBalance (ETH) and bountyBalance (ETH) for an address.
    */
    function balanceOf(address _owner) constant returns (uint256 presaleBalance, uint256 bountyBalance) {
        return (presaleBalances[_owner], bountyBalances[_owner]);
    }

    /*
        If the 'winner' address is address(0), there has been no winner yet.
    */
    modifier onlyWithoutWinner() {
        assert(winner == address(0));
        _;
    }

    /*
        If the 'winner' address is anything other than address(0), there has been a winner.
    */
    modifier onlyWithWinner() {
        assert(winner != address(0));
        _;
    }

    /*
        Throw if our block number is less than the refund start block
    */
    modifier whenRefundIsPermitted() {
        assert(block.number >= refundStartBlock);
        _;
    }

    /*
       Attempt to purchase the tokens from the token contract.
       Whichever address manages to successfully purchase/create the tokens will be the 'winner'.
       The 'winner' is allowed to withdraw the bounties from this contract.
    */
    function buyTokens() external onlyWithoutWinner {
        winner = msg.sender;
        assert(tokenContract.call.value(totalPresale)(bytes4(sha3("createTokens()"))));
    }

    function withdrawTokens() onlyWithWinner {
        uint256 tokens = SafeMath.mul(presaleBalances[msg.sender], tokenExchangeRate);
        assert(tokens > 0);

        totalPresale = SafeMath.sub(totalPresale, presaleBalances[msg.sender]);
        presaleBalances[msg.sender] = 0;

        /*
               Attempt to transfer tokens to msg.sender.
               Note: we are relying on the token contract to return a success bool (true for success). If this
               bool is not implemented as expected it may be possible for an account to withdraw more tokens than
               it is entitled to.
        */
        assert(tokenContract.call(bytes4(sha3('transfer(address,uint256)')), msg.sender, tokens));
        LogWithdrawTokens(msg.sender, tokens);
    }

    /*
        Collect the bounty for successfully executing the token purchase.
    */
    function withdrawBounty() external onlyWithWinner {
        assert(msg.sender == winner);
        assert(totalBounties > 0);

        uint256 entitlement = totalBounties;
        totalBounties = 0;

        msg.sender.transfer(entitlement);
        LogWithdrawBounty(msg.sender, entitlement);
    }

    /*
        Refund an accounts investment and bounty.
        This is only possible if there has not been a 'winner' (ie, if tokens have not been purchased).
    */
    function refundPresaleInvestment() external whenRefundIsPermitted {
        uint256 bountyValue = bountyBalances[msg.sender];
        uint256 presaleValue = presaleBalances[msg.sender];
        uint256 totalValue = SafeMath.add(bountyValue, presaleValue);

        assert(bountyValue > 0);
        assert(presaleValue > 0);

        bountyBalances[msg.sender] = 0;
        presaleBalances[msg.sender] = 0;
        totalBounties = SafeMath.sub(totalBounties, bountyValue);
        totalPresale = SafeMath.sub(totalPresale, presaleValue);
        msg.sender.transfer(totalValue);
        LogRefundPresaleInvestment(msg.sender, bountyValue, presaleValue);
    }
}

pragma solidity ^0.4.10;
import './SafeMath.sol';

contract TokenSyndicate {
    uint256 public constant kwei = 1000;

    address public tokenContract;
    uint256 public tokenExchangeRate;
    uint256 public minBountyPerKwei;
    uint256 public maxPresaleWeiAllowed;
    uint256 public refundStartBlock;
    address public winner;

    uint256 public totalPresale;
    uint256 public totalBounties;

    mapping(address => uint256) public presaleBalances;
    mapping(address => uint256) public bountyBalances;

    event LogCreatePresaleInvestment(address indexed _to, uint256 bounty, uint256 presale);
    event LogRefundPresaleInvestment(address indexed _to, uint256 bounty, uint256 presale);
    event LogWithdrawTokens(address indexed _to, uint256 tokens);
    event LogWithdrawBounty(address indexed _to, uint256 bounty);

    function TokenSyndicate(
    address _tokenContract,
    uint256 _tokenExchangeRate,
    uint256 _minBountyPerKwei,
    uint256 _maxPresaleWeiAllowed,
    uint256 _refundStartBlock){
        assert(_minBountyPerKwei < kwei);       // do not allow a bounty of 100%.
        assert(_minBountyPerKwei > 0);          // do not allow a bounty of zero.
        assert(_maxPresaleWeiAllowed > 0);      // the eth allowed must be greater than zero.
        assert(_tokenContract != address(0));   // the token contract may not be at the zero address.
        assert(_tokenExchangeRate > 0);         // the token exchange rate must not be zero.
        assert(_refundStartBlock >= block.number);   // the refund start time must be now or later

        tokenContract = _tokenContract;
        tokenExchangeRate = _tokenExchangeRate;
        minBountyPerKwei = _minBountyPerKwei;
        maxPresaleWeiAllowed = _maxPresaleWeiAllowed;
        refundStartBlock = _refundStartBlock;

        totalPresale = 0;
        totalBounties = 0;
        winner = address(0);
    }

    /*
        Invest in this contract in order to have tokens purchased on your behalf when the buyTokens() contract
        is called without a `throw`.
    */
    function createPresaleInvestment(uint256 _bountyPerKwei) payable external {
        assert(_bountyPerKwei >= minBountyPerKwei);
        assert(msg.value > 0);

        /*
            As the EVM does not currently support decimals, we are multiplying both msg.value and the bounty
            by 1000 before calculating the bounty ratio in order to gain a reasonable degree of precision.
        */
        uint256 msg_value_with_precision = SafeMath.mul(msg.value, kwei);
        uint256 bounty_with_precision = SafeMath.mul(_bountyPerKwei, SafeMath.div(msg_value_with_precision, kwei));
        uint256 final_bounty = SafeMath.div(bounty_with_precision, kwei);
        uint256 final_presale = SafeMath.sub(msg.value, final_bounty);

        bountyBalances[msg.sender] += final_bounty;
        presaleBalances[msg.sender] += final_presale;
        totalBounties += final_bounty;
        totalPresale += final_presale;
        assert(totalPresale <= maxPresaleWeiAllowed);
        LogCreatePresaleInvestment(msg.sender, final_bounty, final_presale);       // create an event
    }

    /*
        Get the presaleBalance (ETH) and bountyBalance (ETH) for an address.
    */
    function balanceOf(address _owner) constant returns (uint256 presaleBalance, uint256 bountyBalance) {
        return (presaleBalances[_owner], bountyBalances[_owner]);
    }

    /*
        If the 'winner' address is address(0), there has been no winner yet.
    */
    modifier onlyWithoutWinner() {
        assert(winner == address(0));
        _;
    }

    /*
        If the 'winner' address is anything other than address(0), there has been a winner.
    */
    modifier onlyWithWinner() {
        assert(winner != address(0));
        _;
    }

    /*
        Throw if our block number is less than the refund start block
    */
    modifier whenRefundIsPermitted() {
        assert(block.number >= refundStartBlock);
        _;
    }

    /*
       Attempt to purchase the tokens from the token contract.
       Whichever address manages to successfully purchase/create the tokens will be the 'winner'.
       The 'winner' is allowed to withdraw the bounties from this contract.
    */
    function buyTokens() external onlyWithoutWinner {
        winner = msg.sender;
        assert(tokenContract.call.value(totalPresale)(bytes4(sha3("createTokens()"))));
    }

    function withdrawTokens() onlyWithWinner {
        uint256 tokens = SafeMath.mul(presaleBalances[msg.sender], tokenExchangeRate);
        assert(tokens > 0);

        totalPresale = SafeMath.sub(totalPresale, presaleBalances[msg.sender]);
        presaleBalances[msg.sender] = 0;

        /*
               Attempt to transfer tokens to msg.sender.
               Note: we are relying on the token contract to return a success bool (true for success). If this
               bool is not implemented as expected it may be possible for an account to withdraw more tokens than
               it is entitled to.
        */
        assert(tokenContract.call(bytes4(sha3('transfer(address,uint256)')), msg.sender, tokens));
        LogWithdrawTokens(msg.sender, tokens);
    }

    /*
        Collect the bounty for successfully executing the token purchase.
    */
    function withdrawBounty() external onlyWithWinner {
        assert(msg.sender == winner);
        assert(totalBounties > 0);

        uint256 entitlement = totalBounties;
        totalBounties = 0;

        msg.sender.transfer(entitlement);
        LogWithdrawBounty(msg.sender, entitlement);
    }

    /*
        Refund an accounts investment and bounty.
        This is only possible if there has not been a 'winner' (ie, if tokens have not been purchased).
    */
    function refundPresaleInvestment() external whenRefundIsPermitted {
        uint256 bountyValue = bountyBalances[msg.sender];
        uint256 presaleValue = presaleBalances[msg.sender];
        uint256 totalValue = SafeMath.add(bountyValue, presaleValue);

        assert(bountyValue > 0);
        assert(presaleValue > 0);

        bountyBalances[msg.sender] = 0;
        presaleBalances[msg.sender] = 0;
        totalBounties = SafeMath.sub(totalBounties, bountyValue);
        totalPresale = SafeMath.sub(totalPresale, presaleValue);
        msg.sender.transfer(totalValue);
        LogRefundPresaleInvestment(msg.sender, bountyValue, presaleValue);
    }
}