Fixed type

Fixed type

/*
file:   AssetBackedTokens.sol
ver:    0.0.1
updated:15-July-2017
authors: Nick Addison

An ERC20 compliant asset-backed token.

The contract was based off Darryl Morris's ERC 20 token contract
https://github.com/o0ragman0o/ERC20/blob/master/ERC20.sol

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See MIT Licence for further details.
<https://opensource.org/licenses/MIT>.
*/

pragma solidity ^0.4.10;

contract AssetBackedTokens
{
/* Constants */
    bytes32 constant public VERSION = "0.0.1";

/* Structs */
    struct TokenAccount
    {
        // available tokens to the token holder
        uint availableTokens;

        // asset holder of the token holder
        address assetHolder;

        // the amount someone else is allowed to transfer from this token holder
        mapping (address => uint) allowed;

        // flag to indicate if the struct has been initialised. Set to true if it has, otherwise it'll return false
        bool initialised;
    }

    struct AssetAccount
    {
        // the balance of the asset holder's assets held on deposit with the settlement institution
        uint settledAssets;

        // the amount of assets that is to be deposited or withdrawn from the asset holder's account the next time the settlement process is run
        int unsettledAssets;

        // total amount of tokens issued to token holders by this asset holder
        uint issuedTokens;

        // flag to indicate if the struct has been initialised. Set to true if it has, otherwise it'll return false
        bool initialised;
    }

/* State Valiables */

    /// @return Token symbol
    string public symbol;

    /// @return Token symbol
    uint8 public decimals;

    /// @return Total amount of tokens
    uint totalSupply = 0;

    // Externally owned account (token holder's address) mapped to the token account details
    mapping (address => TokenAccount) public tokenAccounts;

    // Maps asset holder addresses to assets held at the settlement institution
    // One asset holder can have multiple addresses mapped to a single account at the settlement institution
    mapping (address => AssetAccount) public assetAccounts;
    // list of participating asset holders
    address[] assetHolders;

    // externally owned account (identity) of the settlement insitituion
    address settlementInstitution;

    // externally owned account (identity) of the scheme administrator
    address administrator;

/* Enums */

/* Modifiers */
    // check that the sender of the transaction is the scheme administrator
    modifier onlyAdministrator {
        require(administrator == msg.sender);
        _;
    }

    // check that the sender of the transaction is the settlement asset holder
    modifier onlySettlementInstitution {
        require(settlementInstitution == msg.sender);
        _;
    }

    // is the transaction originator a registered asset holder?
    modifier onlyAssetHolders {
        require(assetAccounts[msg.sender].initialised);
        _;
    }

/* Events */
    // Triggered when tokens are transferred.
    event Transfer(
        address indexed sendingTokenHolder,
        address indexed receivingTokenHolder,
        uint amount);

    // Triggered whenever approve(address _spender, uint _amount) is called.
    event Approval(
        address indexed tokenHolder,
        address indexed thirdParty,
        uint amount);

    event EmitTokenTransfer(
        address indexed tokenHolder,
        int amount,                 // amount of tokens being increased (positive) or decreased (negative)
        uint availableTokens);      // token holder's available tokens after the update

    event EmitAssetTransfer(
        address assetHolder,
        int amount,                 // the amount of assets transfered to (positive) or from (negative) the asset holder
        int unsettledAssets,        // the asset holder's unsettled assets after the asset transfer
        uint issuedTokens);         // the asset holder's issued tokens after the asset transfer

    event EmitAssetUpdate(
        address indexed assetHolder,
        int amount,                 // amount of assets being increased (positive) or decreased (negative)
        int unsettledAssets);       // the asset holder's unsettled assets after the update

    event EmitTokenUpdate(
        address indexed assetHolder,
        address indexed tokenHolder,
        int amount,                 // amount of tokens being increased (positive) or decreased (negative)
        uint availableTokens,       // token holder's available tokens after the update
        uint issuedTokens);         // asset holder's issued tokens after the update

    event EmitAssetSettlement(
        address indexed assetHolder,
        int settledAssetsInTransaction, // the amount of assets settled for a asset holder. Can be a negative value
        uint newSettledAssetBalance);   // the asset holder's settled assets after the settlement

/* Funtions Public */

    // constructor
    function AssetBackedTokens(string _symbol, uint8 _decimals, address _settlementInstitution)
    {
        symbol = _symbol;
        decimals = _decimals;

        administrator = msg.sender;
        settlementInstitution = _settlementInstitution;
    }

    // balance of the token holder's available tokens
    function balanceOf(address _tokenHolder) public constant returns (uint)
    {
        return tokenAccounts[_tokenHolder].availableTokens;
    }

    // the amount of tokens a third party can transfer from a token holder
    function allowance(address tokenHolder, address thirdParty) public constant returns (uint)
    {
        return tokenAccounts[tokenHolder].allowed[thirdParty];
    }

    // transfers tokens from transaction originator to another token holder
    function transfer(address receivingTokenHolder, uint amount) external
    {
        xfer(msg.sender, receivingTokenHolder, amount);
    }

    // transfer tokens from one tokenHolder to another where the sending token holder has allowed the transaction originator
    function transferFrom(address sendingTokenHolder, address receivingTokenHolder, uint amount) external
    {
        require(amount <= tokenAccounts[sendingTokenHolder].allowed[msg.sender]);

        tokenAccounts[sendingTokenHolder].allowed[msg.sender] -= amount;

        xfer(sendingTokenHolder, receivingTokenHolder, amount);
    }

    // Process a transfer internally.
    function xfer(address sendingTokenHolder, address receivingTokenHolder, uint amount) internal
    {
        // transfer amount is required to be less than or equal to the token holder's available tokens
        require(amount > 0 && amount <= tokenAccounts[sendingTokenHolder].availableTokens);
        // the receiving token holder already has a token account
        require(tokenAccounts[receivingTokenHolder].initialised);

        var sendingAssetHolder = tokenAccounts[sendingTokenHolder].assetHolder;
        var receivingAssetHolder = tokenAccounts[receivingTokenHolder].assetHolder;

        tokenAccounts[sendingTokenHolder].availableTokens -= amount;
        tokenAccounts[receivingTokenHolder].availableTokens += amount;

        // if assets are being transferred between asset holders
        if (sendingAssetHolder != receivingAssetHolder)
        {
            transferAssets(sendingAssetHolder, receivingAssetHolder, amount);
        }

        // Emit event
        Transfer(sendingTokenHolder, receivingTokenHolder, amount);

        // Emit event for sending token holder
        EmitTokenTransfer(sendingTokenHolder, -int(amount), tokenAccounts[sendingTokenHolder].availableTokens);

        // Emit event for receiving token holder
        EmitTokenTransfer(receivingTokenHolder, int(amount), tokenAccounts[receivingTokenHolder].availableTokens);
    }

    function transferAssets(address sendingAssetHolder, address receivingAssetHolder, uint amount) internal onlySettlementInstitution
    {
        // the asset holder of the sending token holder has enough assets and issued tokens
        require(int(assetAccounts[sendingAssetHolder].settledAssets) + assetAccounts[sendingAssetHolder].unsettledAssets >= int(amount) );
        require(assetAccounts[sendingAssetHolder].issuedTokens >= amount);

        // decrease the assets for the sending asset holder
        assetAccounts[sendingAssetHolder].unsettledAssets -= int(amount);
        assetAccounts[sendingAssetHolder].issuedTokens -= amount;

        // increase the assets for the receiving asset holder
        assetAccounts[receivingAssetHolder].unsettledAssets += int(amount);
        assetAccounts[receivingAssetHolder].issuedTokens += amount;

        // Emit events to the sending and receiving asset holders
        EmitAssetTransfer(sendingAssetHolder, int(-amount), assetAccounts[sendingAssetHolder].unsettledAssets, assetAccounts[sendingAssetHolder].issuedTokens);
        EmitAssetTransfer(receivingAssetHolder, int(amount), assetAccounts[receivingAssetHolder].unsettledAssets, assetAccounts[receivingAssetHolder].issuedTokens);
    }

    // Sets the amount of tokens a third-party can transfer from the token holder
    function approve(address thirdParty, uint amount) external
    {
        tokenAccounts[msg.sender].allowed[thirdParty] = amount;
        Approval(msg.sender, thirdParty, amount);
    }

    // the scheme administrator updates the settlement institution
    function updateSettlementInstitution(address newSettlementInstitutionAddress) onlyAdministrator
    {
        settlementInstitution = newSettlementInstitutionAddress;
    }

    // the settlement institution increases (deposit) or decreases (withdrawal) a asset holder's assets on deposit with them
    // a positive amount is a deposit, a negative amount is a withdrawal
    function updateAssets(address assetHolder, int amount) onlySettlementInstitution
    {
        // if a asset account does not already exist for the asset holder
        if (assetAccounts[assetHolder].initialised == false)
        {
            // create a new asset account for the asset holder
            assetAccounts[assetHolder] = AssetAccount(0, 0, 0, true);
            // add to the list of asset holders
            assetHolders.push(assetHolder);
        }

        // the asset holder's remaining assets is required to be greater than or equal to the tokens issued by the asset holder
        require(
            int(assetAccounts[assetHolder].settledAssets) +
            assetAccounts[assetHolder].unsettledAssets +
            amount >= int(assetAccounts[assetHolder].issuedTokens) );

        // increase or decrease the unsettled assets
        assetAccounts[assetHolder].unsettledAssets += amount;

        // Emit event for the update of the asset holder's unsettled assets
        EmitAssetUpdate(assetHolder, amount, assetAccounts[assetHolder].unsettledAssets);
    }

    // the settlement institution settles the assets between their asset holder accounts
    function settleAssets() onlySettlementInstitution
    {
        // for each asset holder
        for (uint i; i < assetHolders.length; i++)
        {
            var assetHolder = assetHolders[i];
            var unsettledAssets = assetAccounts[assetHolder].unsettledAssets;

            // if the asset holder has unsettled assets
            if (unsettledAssets != 0)
            {
                // increase or decrease the settled assets by the number of unsettled assets
                if (unsettledAssets > 0)
                {
                    // TODO need to handle overflow
                    assetAccounts[assetHolder].settledAssets += uint(unsettledAssets);
                }
                else {
                    // TODO need to handle overflow
                    // convert the negative unsettled amount to a positive amount
                    assetAccounts[assetHolder].settledAssets -= uint(-unsettledAssets);
                }

                // reset the unsettled assets back to zero
                assetAccounts[assetHolder].unsettledAssets = 0;

                // emit event
                EmitAssetSettlement(assetHolder, unsettledAssets, assetAccounts[assetHolder].settledAssets);
            }
        }
    }

    // returns the asset balances of an asset holder
    function getAssetBalances(address assetHolder) constant returns (uint settledAssets, int unsettledAssets, uint issuedTokens)
    {
        require(assetAccounts[assetHolder].initialised);

        settledAssets = assetAccounts[assetHolder].settledAssets;
        unsettledAssets = assetAccounts[assetHolder].unsettledAssets;
        issuedTokens = assetAccounts[assetHolder].issuedTokens;
    }

    // asset holder increases or decreases the token holder's available tokens and their own number of issued tokens
    function updateTokens(address tokenHolder, int amount) onlyAssetHolders
    {
        require(amount != 0);

        // if the token account does not already exist
        if (tokenAccounts[tokenHolder].initialised == false)
        {
            tokenAccounts[tokenHolder] = TokenAccount(0, msg.sender, true);
        }

        // is the transaction originator the asset holder of the token holder?
        require(tokenAccounts[tokenHolder].assetHolder == msg.sender);

        address assetHolder = msg.sender;
        uint uintAmount;

        // if increasing (depositing) tokens
        if (amount > 0)
        {
            uintAmount = uint(amount);

            // TODO need to check for overflow
            // the asset holder has enough assets held at the settlement institution to issue more
            // tokens to the asset holder's token holders
            require(int(assetAccounts[assetHolder].settledAssets) +
                assetAccounts[assetHolder].unsettledAssets >= int(assetAccounts[assetHolder].issuedTokens) + amount);

            assetAccounts[assetHolder].issuedTokens += uintAmount;
            tokenAccounts[tokenHolder].availableTokens += uintAmount;
        }
        // decreasing (withdrawing) tokens
        else if (amount < 0)
        {
            uintAmount = uint(-amount);

            // the token holder's available tokens is required to be greater than the amount of tokens being withdrawn
            require(tokenAccounts[tokenHolder].availableTokens > uintAmount);

            assetAccounts[assetHolder].issuedTokens -= uintAmount;
            tokenAccounts[tokenHolder].availableTokens -= uintAmount;
        }

        EmitTokenUpdate(
            assetHolder,
            tokenHolder,
            amount,                                     // amount of tokens
            tokenAccounts[tokenHolder].availableTokens, // updated value
            assetAccounts[assetHolder].issuedTokens);   // updated value
    }

    // removes any asset holders from the list of asset holders if they have zero asset balances
    // this is done peridically rather than inline as it is an expensive operation
    function cleanAssetHolders() returns (uint numberOfCleanedAssetHolders)
    {
        address[] storage newassetHolders;

        // for each asset holder
        for (uint i; i < assetHolders.length; i++)
        {
            var assetHolder = assetHolders[i];

            // if the asset holder no longer has any positive or negative balances
            if (assetAccounts[assetHolder].unsettledAssets == 0 &&
                assetAccounts[assetHolder].settledAssets == 0 &&
                assetAccounts[assetHolder].issuedTokens == 0)
            {
                // Do not add this old asset holder with zero balances to the list of new asset holders
                numberOfCleanedAssetHolders++;
            }
            else
            {
                // add the old asset holder into the list of new asset holders
                newassetHolders.push(assetHolder);
            }
        }

        assetHolders = newassetHolders;
    }
}

/*
file:   AssetBackedTokens.sol
ver:    0.0.1
updated:15-July-2017
authors: Nick Addison

An ERC20 compliant asset-backed token.

The contract was based off Darryl Morris's ERC 20 token contract
https://github.com/o0ragman0o/ERC20/blob/master/ERC20.sol

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See MIT Licence for further details.
<https://opensource.org/licenses/MIT>.
*/

pragma solidity ^0.4.10;

contract AssetBackedTokens
{
/* Constants */
    bytes32 constant public VERSION = "0.0.1";

/* Structs */
    struct TokenAccount
    {
        // available tokens to the token holder
        uint availableTokens;

        // asset holder of the token holder
        address assetHolder;

        // the amount someone else is allowed to transfer from this token holder
        mapping (address => uint) allowed;

        // flag to indicate if the struct has been initialised. Set to true if it has, otherwise it'll return false
        bool initialised;
    }

    struct AssetAccount
    {
        // the balance of the asset holder's assets held on deposit with the settlement institution
        uint settledAssets;

        // the amount of assets that is to be deposited or withdrawn from the asset holder's account the next time the settlement process is run
        int unsettledAssets;

        // total amount of tokens issued to token holders by this asset holder
        uint issuedTokens;

        // flag to indicate if the struct has been initialised. Set to true if it has, otherwise it'll return false
        bool initialised;
    }

/* State Valiables */

    /// @return Token symbol
    string public symbol;

    /// @return Token symbol
    uint8 public decimals;

    /// @return Total amount of tokens
    uint totalSupply = 0;

    // Externally owned account (token holder's address) mapped to the token account details
    mapping (address => TokenAccount) public tokenAccounts;

    // Maps asset holder addresses to assets held at the settlement institution
    // One asset holder can have multiple addresses mapped to a single account at the settlement institution
    mapping (address => AssetAccount) public assetAccounts;
    // list of participating asset holders
    address[] assetHolders;

    // externally owned account (identity) of the settlement insitituion
    address settlementInstitution;

    // externally owned account (identity) of the scheme administrator
    address administrator;

/* Enums */

/* Modifiers */
    // check that the sender of the transaction is the scheme administrator
    modifier onlyAdministrator {
        require(administrator == msg.sender);
        _;
    }

    // check that the sender of the transaction is the settlement asset holder
    modifier onlySettlementInstitution {
        require(settlementInstitution == msg.sender);
        _;
    }

    // is the transaction originator a registered asset holder?
    modifier onlyAssetHolders {
        require(assetAccounts[msg.sender].initialised);
        _;
    }

/* Events */
    // Triggered when tokens are transferred.
    event Transfer(
        address indexed sendingTokenHolder,
        address indexed receivingTokenHolder,
        uint amount);

    // Triggered whenever approve(address _spender, uint _amount) is called.
    event Approval(
        address indexed tokenHolder,
        address indexed thirdParty,
        uint amount);

    event EmitTokenTransfer(
        address indexed tokenHolder,
        int amount,                 // amount of tokens being increased (positive) or decreased (megative)
        uint availableTokens);      // token holder's available tokens after the update

    event EmitAssetTransfer(
        address assetHolder,
        int amount,                 // the amount of assets transfered to (positive) or from (negative) the asset holder
        int unsettledAssets,        // the asset holder's unsettled assets after the asset transfer
        uint issuedTokens);         // the asset holder's issued tokens after the asset transfer

    event EmitAssetUpdate(
        address indexed assetHolder,
        int amount,                 // amount of assets being increased (positive) or decreased (negative)
        int unsettledAssets);       // the asset holder's unsettled assets after the update

    event EmitTokenUpdate(
        address indexed assetHolder,
        address indexed tokenHolder,
        int amount,                 // amount of tokens being increased (positive) or decreased (megative)
        uint availableTokens,       // token holder's available tokens after the update
        uint issuedTokens);         // asset holder's issued tokens after the update

    event EmitAssetSettlement(
        address indexed assetHolder,
        int settledAssetsInTransaction, // the amount of assets settled for a asset holder. Can be a negative value
        uint newSettledAssetBalance);   // the asset holder's settled assets after the settlement

/* Funtions Public */

    // constructor
    function AssetBackedTokens(string _symbol, uint8 _decimals, address _settlementInstitution)
    {
        symbol = _symbol;
        decimals = _decimals;

        administrator = msg.sender;
        settlementInstitution = _settlementInstitution;
    }

    // balance of the token holder's available tokens
    function balanceOf(address _tokenHolder) public constant returns (uint)
    {
        return tokenAccounts[_tokenHolder].availableTokens;
    }

    // the amount of tokens a third party can transfer from a token holder
    function allowance(address tokenHolder, address thirdParty) public constant returns (uint)
    {
        return tokenAccounts[tokenHolder].allowed[thirdParty];
    }

    // transfers tokens from transaction originator to another token holder
    function transfer(address receivingTokenHolder, uint amount) external
    {
        xfer(msg.sender, receivingTokenHolder, amount);
    }

    // transfer tokens from one tokenHolder to another where the sending token holder has allowed the transaction originator
    function transferFrom(address sendingTokenHolder, address receivingTokenHolder, uint amount) external
    {
        require(amount <= tokenAccounts[sendingTokenHolder].allowed[msg.sender]);

        tokenAccounts[sendingTokenHolder].allowed[msg.sender] -= amount;

        xfer(sendingTokenHolder, receivingTokenHolder, amount);
    }

    // Process a transfer internally.
    function xfer(address sendingTokenHolder, address receivingTokenHolder, uint amount) internal
    {
        // transfer amount is required to be less than or equal to the token holder's available tokens
        require(amount > 0 && amount <= tokenAccounts[sendingTokenHolder].availableTokens);
        // the receiving token holder already has a token account
        require(tokenAccounts[receivingTokenHolder].initialised);

        var sendingAssetHolder = tokenAccounts[sendingTokenHolder].assetHolder;
        var receivingAssetHolder = tokenAccounts[receivingTokenHolder].assetHolder;

        tokenAccounts[sendingTokenHolder].availableTokens -= amount;
        tokenAccounts[receivingTokenHolder].availableTokens += amount;

        // if assets are being transferred between asset holders
        if (sendingAssetHolder != receivingAssetHolder)
        {
            transferAssets(sendingAssetHolder, receivingAssetHolder, amount);
        }

        // Emit event
        Transfer(sendingTokenHolder, receivingTokenHolder, amount);

        // Emit event for sending token holder
        EmitTokenTransfer(sendingTokenHolder, -int(amount), tokenAccounts[sendingTokenHolder].availableTokens);

        // Emit event for receiving token holder
        EmitTokenTransfer(receivingTokenHolder, int(amount), tokenAccounts[receivingTokenHolder].availableTokens);
    }

    function transferAssets(address sendingAssetHolder, address receivingAssetHolder, uint amount) internal onlySettlementInstitution
    {
        // the asset holder of the sending token holder has enough assets and issued tokens
        require(int(assetAccounts[sendingAssetHolder].settledAssets) + assetAccounts[sendingAssetHolder].unsettledAssets >= int(amount) );
        require(assetAccounts[sendingAssetHolder].issuedTokens >= amount);

        // decrease the assets for the sending asset holder
        assetAccounts[sendingAssetHolder].unsettledAssets -= int(amount);
        assetAccounts[sendingAssetHolder].issuedTokens -= amount;

        // increase the assets for the receiving asset holder
        assetAccounts[receivingAssetHolder].unsettledAssets += int(amount);
        assetAccounts[receivingAssetHolder].issuedTokens += amount;

        // Emit events to the sending and receiving asset holders
        EmitAssetTransfer(sendingAssetHolder, int(-amount), assetAccounts[sendingAssetHolder].unsettledAssets, assetAccounts[sendingAssetHolder].issuedTokens);
        EmitAssetTransfer(receivingAssetHolder, int(amount), assetAccounts[receivingAssetHolder].unsettledAssets, assetAccounts[receivingAssetHolder].issuedTokens);
    }

    // Sets the amount of tokens a third-party can transfer from the token holder
    function approve(address thirdParty, uint amount) external
    {
        tokenAccounts[msg.sender].allowed[thirdParty] = amount;
        Approval(msg.sender, thirdParty, amount);
    }

    // the scheme administrator updates the settlement institution
    function updateSettlementInstitution(address newSettlementInstitutionAddress) onlyAdministrator
    {
        settlementInstitution = newSettlementInstitutionAddress;
    }

    // the settlement institution increases (deposit) or decreases (withdrawal) a asset holder's assets on deposit with them
    // a positive amount is a deposit, a negative amount is a withdrawal
    function updateAssets(address assetHolder, int amount) onlySettlementInstitution
    {
        // if a asset account does not already exist for the asset holder
        if (assetAccounts[assetHolder].initialised == false)
        {
            // create a new asset account for the asset holder
            assetAccounts[assetHolder] = AssetAccount(0, 0, 0, true);
            // add to the list of asset holders
            assetHolders.push(assetHolder);
        }

        // the asset holder's remaining assets is required to be greater than or equal to the tokens issued by the asset holder
        require(
            int(assetAccounts[assetHolder].settledAssets) +
            assetAccounts[assetHolder].unsettledAssets +
            amount >= int(assetAccounts[assetHolder].issuedTokens) );

        // increase or decrease the unsettled assets
        assetAccounts[assetHolder].unsettledAssets += amount;

        // Emit event for the update of the asset holder's unsettled assets
        EmitAssetUpdate(assetHolder, amount, assetAccounts[assetHolder].unsettledAssets);
    }

    // the settlement institution settles the assets between their asset holder accounts
    function settleAssets() onlySettlementInstitution
    {
        // for each asset holder
        for (uint i; i < assetHolders.length; i++)
        {
            var assetHolder = assetHolders[i];
            var unsettledAssets = assetAccounts[assetHolder].unsettledAssets;

            // if the asset holder has unsettled assets
            if (unsettledAssets != 0)
            {
                // increase or decrease the settled assets by the number of unsettled assets
                if (unsettledAssets > 0)
                {
                    // TODO need to handle overflow
                    assetAccounts[assetHolder].settledAssets += uint(unsettledAssets);
                }
                else {
                    // TODO need to handle overflow
                    // convert the negative unsettled amount to a positive amount
                    assetAccounts[assetHolder].settledAssets -= uint(-unsettledAssets);
                }

                // reset the unsettled assets back to zero
                assetAccounts[assetHolder].unsettledAssets = 0;

                // emit event
                EmitAssetSettlement(assetHolder, unsettledAssets, assetAccounts[assetHolder].settledAssets);
            }
        }
    }

    // returns the asset balances of an asset holder
    function getAssetBalances(address assetHolder) constant returns (uint settledAssets, int unsettledAssets, uint issuedTokens)
    {
        require(assetAccounts[assetHolder].initialised);

        settledAssets = assetAccounts[assetHolder].settledAssets;
        unsettledAssets = assetAccounts[assetHolder].unsettledAssets;
        issuedTokens = assetAccounts[assetHolder].issuedTokens;
    }

    // asset holder increases or decreases the token holder's available tokens and their own number of issued tokens
    function updateTokens(address tokenHolder, int amount) onlyAssetHolders
    {
        require(amount != 0);

        // if the token account does not already exist
        if (tokenAccounts[tokenHolder].initialised == false)
        {
            tokenAccounts[tokenHolder] = TokenAccount(0, msg.sender, true);
        }

        // is the transaction originator the asset holder of the token holder?
        require(tokenAccounts[tokenHolder].assetHolder == msg.sender);

        address assetHolder = msg.sender;
        uint uintAmount;

        // if increasing (depositing) tokens
        if (amount > 0)
        {
            uintAmount = uint(amount);

            // TODO need to check for overflow
            // the asset holder has enough assets held at the settlement institution to issue more
            // tokens to the asset holder's token holders
            require(int(assetAccounts[assetHolder].settledAssets) +
                assetAccounts[assetHolder].unsettledAssets >= int(assetAccounts[assetHolder].issuedTokens) + amount);

            assetAccounts[assetHolder].issuedTokens += uintAmount;
            tokenAccounts[tokenHolder].availableTokens += uintAmount;
        }
        // decreasing (withdrawing) tokens
        else if (amount < 0)
        {
            uintAmount = uint(-amount);

            // the token holder's available tokens is required to be greater than the amount of tokens being withdrawn
            require(tokenAccounts[tokenHolder].availableTokens > uintAmount);

            assetAccounts[assetHolder].issuedTokens -= uintAmount;
            tokenAccounts[tokenHolder].availableTokens -= uintAmount;
        }

        EmitTokenUpdate(
            assetHolder,
            tokenHolder,
            amount,                                     // amount of tokens
            tokenAccounts[tokenHolder].availableTokens, // updated value
            assetAccounts[assetHolder].issuedTokens);   // updated value
    }

    // removes any asset holders from the list of asset holders if they have zero asset balances
    // this is done peridically rather than inline as it is an expensive operation
    function cleanAssetHolders() returns (uint numberOfCleanedAssetHolders)
    {
        address[] storage newassetHolders;

        // for each asset holder
        for (uint i; i < assetHolders.length; i++)
        {
            var assetHolder = assetHolders[i];

            // if the asset holder no longer has any positive or negative balances
            if (assetAccounts[assetHolder].unsettledAssets == 0 &&
                assetAccounts[assetHolder].settledAssets == 0 &&
                assetAccounts[assetHolder].issuedTokens == 0)
            {
                // Do not add this old asset holder with zero balances to the list of new asset holders
                numberOfCleanedAssetHolders++;
            }
            else
            {
                // add the old asset holder into the list of new asset holders
                newassetHolders.push(assetHolder);
            }
        }

        assetHolders = newassetHolders;
    }
}


adding some notes and finished allocating positions upon trade occuring

/*
    Copyright 2017 Phillip A. Elsasser

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

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

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

pragma solidity 0.4.18;

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

// TODO:
//      add failsafe for pool distribution.
//      push as much into library as possible
//      think about circuit breaker in case of issues
//      discuss build out ability to use ETH vs only ERC20? - WETH?
//      add open interest to allow users to see outstanding open positions
//      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 HashLib for address;
    using SafeERC20 for ERC20;

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

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    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 indexed orderHash
    );
    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 maker address of the maker of this order
    /// @param taker address of the designated taker or address(0) if publicly available to be traded
    /// @param feeRecipient address of a fee recipient, optional
    /// @param makerFee amount of MKT token for maker fee
    /// @param takerFee amount of MKT token for taker fee
    /// @param price of the order
    /// @param orderQty quantity of the order
    /// @param qtyToFill quantity taker is willing to fill of original order(max)
    /// @param expirationTimeStamp last valid timestamp of the order (seconds since epoch)
    /// @param v order signature
    /// @param r order signature
    /// @param s order signature
    function tradeOrder(
        address maker,
        address taker,
        address feeRecipient,
        uint makerFee,
        uint takerFee,
        uint price,
        int orderQty,
        int qtyToFill,
        uint expirationTimeStamp,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (int filledQty) {
        require(!isSettled);                                // no trading past settlement
        require(taker == address(0) || taker == msg.sender);// taker can be anyone, or specifically the caller!
        require(maker != address(0) && maker != taker);     // do not allow self trade
        require(orderQty != 0 && qtyToFill != 0);           // no zero trades
        require(orderQty.isSameSign(qtyToFill));            // signs should match

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

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

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

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

        filledQty = MathLib.absMin(remainingQty, qtyToFill);
        updatePositions(maker, taker, filledQty, price);    // this will fail if any of the balances are insufficient

        uint paidMakerFee = 0;
        uint paidTakerFee = 0;

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

            if(makerFee > 0) {

            }

            if(takerFee > 0) {

            }
        }

        OrderFilled(maker, taker, feeRecipient, filledQty, paidMakerFee, paidTakerFee, orderHash);
        return filledQty;
    }

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

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

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

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

        qtyCancelled = MathLib.absMin(qtyToCancel, remainingQty);   // we can only cancel what remains
        cancelledOrderQty[orderHash] = cancelledOrderQty[orderHash].add(qtyCancelled);
        OrderCancelled(maker, feeRecipient, cancelledQty, 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.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(userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                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(this, 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(this, position.qty, price));
                qtyToReduce = qtyToReduce.add(position.qty);
                userNetPos.positions.length--;  // remove this position from our array.
            }
            else {  // this position stays, just reduce the qty.
                position.qty = position.qty.add(qtyToReduce);
                // pos is opp sign of qty we are reducing here!
                collateralToReturnToUserAccount.add(MathLib.calculateNeededCollateral(this, qtyToReduce * -1, price));
                //qtyToReduce = 0; // completely reduced now!
                break;
            }
        }

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

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

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

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

    /// @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.
    /// @returns 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/HashLib.sol";
import "zeppelin-solidity/contracts/token/ERC20.sol";
import "zeppelin-solidity/contracts/token/SafeERC20.sol";

// TODO:
//      add failsafe for pool distribution.
//      push as much into library as possible
//      think about circuit breaker in case of issues
//      discuss build out ability to use ETH vs only ERC20? - WETH?
//      add open interest to allow users to see outstanding open positions
//      add function to allow user to pay for an extra query right now that could trigger settlement.
//      require creator to own arbitrary amount of MEM
//      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 HashLib for address;
    using SafeERC20 for ERC20;

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

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

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

    /*
    // EXTERNAL METHODS
    */

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

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

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

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

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

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

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

        filledQty = MathLib.absMin(remainingQty, qtyToFill);
        //TODO: finish build out and allocation of positions.

        uint paidMakerFee = 0;
        uint paidTakerFee = 0;
        OrderFilled(maker, taker, feeRecipient, filledQty, paidMakerFee, paidTakerFee, orderHash);
        return filledQty;
    }

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

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

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

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

        qtyCancelled = MathLib.absMin(qtyToCancel, remainingQty);   // we can only cancel what remains
        cancelledOrderQty[orderHash] = cancelledOrderQty[orderHash].add(qtyCancelled);
        OrderCancelled(maker, feeRecipient, cancelledQty, 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.netPosition * - 1, settlementPrice);
        }
        withdrawTokens(userAddressToAccountBalance[msg.sender]);   // transfer all balances back to user.
    }

    /*
    // PUBLIC METHODS
    */

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

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

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

    /*
    // PRIVATE METHODS
    */

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

    }

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

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

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

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

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

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

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

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


no storage?

no storage?

pragma solidity ^0.4.13;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

    mapping(string => Pot) nameToPot;
    mapping(address => uint) public refunds;
    uint public totalPendingRefunds;

    event LogPotCreated(string name, uint buyIn);
    event LogPotJoined(string name, address indexed newPlayer, uint buyIn);
    event LogPotFull(string name);
    event LogPotExpired(string name);
    event LogPotClosed(string name);
    event LogPotWinner(string name, address indexed winner, uint refundAmount);
    event LogPotLoser(string name, address indexed loser);
    event LogAccountRefund(address indexed account, uint refundAmount);

    function PotOfEther() {
        owner = msg.sender;
    }

    function availableOwnerWithdraw() constant returns (uint){
        return this.balance - totalPendingRefunds;
    }

    function ownerWithdraw(){
        owner.transfer(availableOwnerWithdraw());
    }

    function createPot(string name) payable {
        require(msg.value > 0); // must bet something
        require(bytes(name).length > 0); // name can't be empty
        require(nameToPot[name].buyIn == 0); // there isn't already a pot with this name

        Pot storage pot = nameToPot[name];
        pot.name = name;
        pot.buyIn = msg.value;
        pot.players.push(msg.sender);
        pot.isOpen = true;

        totalPendingRefunds += pot.buyIn; // owner can't withdraw this funds while pot is open

        LogPotCreated(name, msg.value);
        LogPotJoined(name, msg.sender, msg.value);
    }

    function joinPot(string name) payable {
        Pot storage pot = nameToPot[name];
        require(pot.isOpen); // pot exists and isn't over
        require(pot.players.length < 3); // pot isn't full
        require(msg.value == pot.buyIn); // must pay buyIn amount
        for(uint i = 0; i < pot.players.length; i++){
            require(pot.players[i] != msg.sender); //must be new to this pot
        }

        pot.players.push(msg.sender);
        LogPotJoined(name, msg.sender, msg.value);

        totalPendingRefunds += pot.buyIn; // owner can't withdraw this funds while pot is open

        if(pot.players.length == 3){
            pot.lastPlayerBlockNumber = block.number;
            LogPotFull(name);
        }
    }

    function canClosePot(string name) constant returns (bool){
        Pot storage pot = nameToPot[name];
        return pot.isOpen &&
                pot.players.length == 3 &&
                block.number > pot.lastPlayerBlockNumber + 1;
    }

    function closePot(string name){
        Pot storage pot = nameToPot[name];
        require(pot.isOpen); // pot isn't closed
        require(pot.players.length == 3); // pot full
        require(block.number > pot.lastPlayerBlockNumber + 1);

        pot.isOpen = false;
        LogPotClosed(name);
        totalPendingRefunds -= (pot.buyIn * 3);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - nobody closed the pot
            LogPotExpired(name);

            // return money - no fee
            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += pot.buyIn;
                totalPendingRefunds += pot.buyIn;
            }

            return;
        }

        bytes32 potShaResult = sha3(name, blockHash);
        uint8 loserIndex = uint8(uint256(potShaResult) % 3);

        address loser = pot.players[loserIndex];
        address winner1 = pot.players[(loserIndex + 1) % 3];
        address winner2 = pot.players[(loserIndex + 2) % 3];

        uint fee = pot.buyIn / 100;
        uint playerWinAmount = (pot.buyIn - fee) / 2; // take 1% fee and split winnins
        uint winnerRefundAmount = pot.buyIn + playerWinAmount;

        refunds[winner1] += winnerRefundAmount;
        totalPendingRefunds += winnerRefundAmount;

        refunds[winner2] += winnerRefundAmount;
        totalPendingRefunds += winnerRefundAmount;

        LogPotWinner(name, winner1, winnerRefundAmount);
        LogPotWinner(name, winner2, winnerRefundAmount);
        LogPotLoser(name, loser);
    }

    function withdrawRefund(){
        uint refund = refunds[msg.sender];
        refunds[msg.sender] = 0;
        totalPendingRefunds -= refund;

        msg.sender.transfer(refund);
        LogAccountRefund(msg.sender, refund);
    }
}

pragma solidity ^0.4.13;
contract PotOfEther {

    struct Pot {
        string storage name;
        uint buyIn;

        address[] storage players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

    mapping(string => Pot) nameToPot;
    mapping(address => uint) public refunds;
    uint public totalPendingRefunds;

    event LogPotCreated(string name, uint buyIn);
    event LogPotJoined(string name, address indexed newPlayer, uint buyIn);
    event LogPotFull(string name);
    event LogPotExpired(string name);
    event LogPotClosed(string name);
    event LogPotWinner(string name, address indexed winner, uint refundAmount);
    event LogPotLoser(string name, address indexed loser);
    event LogAccountRefund(address indexed account, uint refundAmount);

    function PotOfEther() {
        owner = msg.sender;
    }

    function availableOwnerWithdraw() constant returns (uint){
        return this.balance - totalPendingRefunds;
    }

    function ownerWithdraw(){
        owner.transfer(availableOwnerWithdraw());
    }

    function createPot(string name) payable {
        require(msg.value > 0); // must bet something
        require(bytes(name).length > 0); // name can't be empty
        require(nameToPot[name].buyIn == 0); // there isn't already a pot with this name

        Pot storage pot = nameToPot[name];
        pot.name = name;
        pot.buyIn = msg.value;
        pot.players.push(msg.sender);
        pot.isOpen = true;

        totalPendingRefunds += pot.buyIn; // owner can't withdraw this funds while pot is open

        LogPotCreated(name, msg.value);
        LogPotJoined(name, msg.sender, msg.value);
    }

    function joinPot(string name) payable {
        Pot storage pot = nameToPot[name];
        require(pot.isOpen); // pot exists and isn't over
        require(pot.players.length < 3); // pot isn't full
        require(msg.value == pot.buyIn); // must pay buyIn amount
        for(uint i = 0; i < pot.players.length; i++){
            require(pot.players[i] != msg.sender); //must be new to this pot
        }

        pot.players.push(msg.sender);
        LogPotJoined(name, msg.sender, msg.value);

        totalPendingRefunds += pot.buyIn; // owner can't withdraw this funds while pot is open

        if(pot.players.length == 3){
            pot.lastPlayerBlockNumber = block.number;
            LogPotFull(name);
        }
    }

    function canClosePot(string name) constant returns (bool){
        Pot storage pot = nameToPot[name];
        return pot.isOpen &&
                pot.players.length == 3 &&
                block.number > pot.lastPlayerBlockNumber + 1;
    }

    function closePot(string name){
        Pot storage pot = nameToPot[name];
        require(pot.isOpen); // pot isn't closed
        require(pot.players.length == 3); // pot full
        require(block.number > pot.lastPlayerBlockNumber + 1);

        pot.isOpen = false;
        LogPotClosed(name);
        totalPendingRefunds -= (pot.buyIn * 3);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - nobody closed the pot
            LogPotExpired(name);

            // return money - no fee
            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += pot.buyIn;
                totalPendingRefunds += pot.buyIn;
            }

            return;
        }

        bytes32 potShaResult = sha3(name, blockHash);
        uint8 loserIndex = uint8(uint256(potShaResult) % 3);

        address loser = pot.players[loserIndex];
        address winner1 = pot.players[(loserIndex + 1) % 3];
        address winner2 = pot.players[(loserIndex + 2) % 3];

        uint fee = pot.buyIn / 100;
        uint playerWinAmount = (pot.buyIn - fee) / 2; // take 1% fee and split winnins
        uint winnerRefundAmount = pot.buyIn + playerWinAmount;

        refunds[winner1] += winnerRefundAmount;
        totalPendingRefunds += winnerRefundAmount;

        refunds[winner2] += winnerRefundAmount;
        totalPendingRefunds += winnerRefundAmount;

        LogPotWinner(name, winner1, winnerRefundAmount);
        LogPotWinner(name, winner2, winnerRefundAmount);
        LogPotLoser(name, loser);
    }

    function withdrawRefund(){
        uint refund = refunds[msg.sender];
        refunds[msg.sender] = 0;
        totalPendingRefunds -= refund;

        msg.sender.transfer(refund);
        LogAccountRefund(msg.sender, refund);
    }
}


-- Merge branch 'master' of https://github.com/hackerhouseparis/abie

/* Part of this contract is from the solidity documentation
TODO: Set a license.
*/

pragma solidity ^0.4.8;

/// @title Voting with delegation.
contract AbieFund {

    uint membershipFee = 0.1 ether;
    uint nbMembers;
    uint registrationTime = 1 years;

    event Donated(address donor, uint amount);

    enum ProposalType {AddMember,FundProject} // Different types of proposals.
    enum VoteType {Abstain,Yes,No} // Different value of a vote.

    struct Proposal
    {
        bytes32 name;       // short name (up to 32 bytes).
        uint voteYes;     // number of YES votes.
        uint voteAbstain;     // number of abstention. Number of No can be deduced.
        address recipient;     // address the funds will be sent.
        uint value;         // quantity of wei to be sent.
        bytes32 data;       // data of the transaction.
        ProposalType proposalType;  // type of the proposal.
        mapping (address => VoteType) vote; // vote of the party.
    }

    struct Member
    {
        uint registration;  // date of registration, if 0 the member does not exist.
        address[2] delegate; // delegate[proposalType] gives the delegate for the type.
    }

    mapping (address => Member) public members;

    Proposal[] public proposals;

    /// Require at least price to be paid.
    modifier costs(uint price) {
        if (msg.value<price)
            throw;
        _;
    }

    /// Require the caller to be a member.
    modifier isMember() {
        if (members[msg.sender].registration==0) // Not a member.
            throw;
        if (members[msg.sender].registration+registrationTime<now) // Has expired.
            throw;
        _;
    }

    /// @param initialMembers First members of the organization.
    function AbieFund(address[] initialMembers) {
        for (uint i;i<initialMembers.length;++i)
            members[initialMembers[i]].registration=now;
        nbMembers=initialMembers.length;
    }

    /** Choose a delegate.
      * @param proposalType 0 for AddMember, 1 for FundProject.
      * @param target account to delegate to.
      */
    function setDelegate(uint8 proposalType, address target)
    {
        members[msg.sender].delegate[proposalType] = target;
    }

    /// Receive funds.
    function () payable {
        Donated(msg.sender, msg.value);
    }

    function askMembership () payable costs(membershipFee) {
        Donated(msg.sender,msg.value); // Register the donation.

        // Create a proposal to add the member.
        proposals.push(Proposal({
        name: 0x0,
        voteYes: 0,
        voteAbstain: 0,
        recipient: msg.sender,
        value: 0x0,
        data: 0x0,
        proposalType: ProposalType.AddMember
        }));
    }

    function vote (uint proposal, VoteType voteType) isMember {
        // TODO
    }

    /// GETTERS ///

    /** Return the delegate.
     *  @param member member to get the delegate from.
     *  @param proposalType 0 for AddMember, 1 for FundProject.
     */
    function getDelegate(address member, uint8 proposalType) constant returns (address){
        return members[member].delegate[proposalType];
    }

}

pragma solidity ^0.4.8;

/// @title Voting with delegation.
contract AbieFund {

    uint membershipFee=0.1 ether;

    event Donated(address indexed donor, uint amount);

    enum ProposalType {AddMember,FundProject} // Different types of proposals.

    struct Proposal
    {
        bytes32 name;       // short name (up to 32 bytes).
        uint voteCount;     // number of accumulated votes.
        address recipient;     // address the funds will be sent.
        uint value;         // quantity of wei to be sent.
        bytes32 data;       // data of the transaction.
        ProposalType proposalType;  // type of the proposal
    }

    struct Member
    {
        uint registration;  // date of registration, if 0 the member does not exist.
        address[2] delegate; // delegate[proposalType] gives the delegate for the type.
    }

    mapping (address => Member) public members;

    Proposal[] public proposals;

    /// Require at least price to be paid.
    modifier costs(uint price) {
        if (msg.value<price)
            throw;
        _;
    }

    /// @param initialMembers First members of the organization.
    function AbieFund(address[] initialMembers) {
        for (uint i;i<initialMembers.length;++i)
            members[initialMembers[i]].registration=now;
    }

    /** Choose a delegate.
      * @param proposalType 0 for AddMember, 1 for FundProject.
      * @param target account to delegate to.
      */
    function setDelegate(uint8 proposalType, address target)
    {
        members[msg.sender].delegate[proposalType] = target;
    }

    /// Receive funds.
    function () payable {
        Donated(msg.sender, msg.value);
    }

    function askMembership () payable costs(membershipFee) {
        Donated(msg.sender,msg.value);
        proposals.push(Proposal({
        name: 0x0,
        voteCount: 0,
        recipient: msg.sender,
        value: 0x0,
        data: 0x0,
        proposalType: ProposalType.AddMember
        }));
    }

    /// GETTERS ///

    /** Return the delegate.
     *  @param member member to get the delegate from.
     *  @param proposalType 0 for AddMember, 1 for FundProject.
     */
    function getDelegate(address member, uint8 proposalType) constant returns (address){
        return members[member].delegate[proposalType];
    }

}

/*
    mapping(address => Voter) public voters;
    // This declares a new complex type which will
    // be used for variables later.
    // It will represent a single voter.
    struct Voter {
        uint weight; // weight is accumulated by delegation.
        bool voted;  // if true, that person already voted.
        address delegate; // person delegated to.
        uint vote;   // index of the voted proposal.
    }

    // This declares a state variable that.
    // stores a `Voter` struct for each possible address.
    mapping(address => Voter) public voters;

    // Give `voter` the right to vote on this ballot.
    // May only be called by `chairperson`.
    function giveRightToVote(address voter) {
        if (msg.sender != chairperson || voters[voter].voted) {
            // `throw` terminates and reverts all changes to
            // the state and to Ether balances. It is often
            // a good idea to use this if functions are
            // called incorrectly. But watch out, this
            // will also consume all provided gas.
            throw;
        }
        voters[voter].weight = 1;
    }

    /// Delegate your vote to the voter `to`.
    function delegate(address to) {
        // assigns reference
        Voter sender = voters[msg.sender];
        if (sender.voted)
            throw;

        // Forward the delegation as long as
        // `to` also delegated.
        // In general, such loops are very dangerous,
        // because if they run too long, they might
        // need more gas than is available in a block.
        // In this case, the delegation will not be executed,
        // but in other situations, such loops might
        // cause a contract to get "stuck" completely.
        while (
            voters[to].delegate != address(0) &&
            voters[to].delegate != msg.sender
        ) {
            to = voters[to].delegate;
        }

        // We found a loop in the delegation, not allowed.
        if (to == msg.sender) {
            throw;
        }

        // Since `sender` is a reference, this
        // modifies `voters[msg.sender].voted`
        sender.voted = true;
        sender.delegate = to;
        Voter delegate = voters[to];
        if (delegate.voted) {
            // If the delegate already voted,
            // directly add to the number of votes
            proposals[delegate.vote].voteCount += sender.weight;
        } else {
            // If the delegate did not vote yet,
            // add to her weight.
            delegate.weight += sender.weight;
        }
    }

    /// Give your vote (including votes delegated to you)
    /// to proposal `proposals[proposal].name`.
    function vote(uint proposal) {
        Voter sender = voters[msg.sender];
        if (sender.voted)
            throw;
        sender.voted = true;
        sender.vote = proposal;

        // If `proposal` is out of the range of the array,
        // this will throw automatically and revert all
        // changes.
        proposals[proposal].voteCount += sender.weight;
    }

    /// @dev Computes the winning proposal taking all
    /// previous votes into account.
    function winningProposal() constant
            returns (uint winningProposal)
    {
        uint winningVoteCount = 0;
        for (uint p = 0; p < proposals.length; p++) {
            if (proposals[p].voteCount > winningVoteCount) {
                winningVoteCount = proposals[p].voteCount;
                winningProposal = p;
            }
        }
    }

    // Calls winningProposal() function to get the index
    // of the winner contained in the proposals array and then
    // returns the name of the winner
    function winnerName() constant
            returns (bytes32 winnerName)
    {
        winnerName = proposals[winningProposal()].name;
    }

*/