changes to DRS

changes to DRS

pragma solidity 0.4.15;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/token/StandardToken.sol';

/**
* Health Decentralized Record Service (DRS)
* This contract enables creation of url/key sets which can
* be managed, shared, traded, and sold using Health Cash (HLTH).
*
* These url/key sets enable data gatekeeper services and
* cryptographically secure data exchanges.
*/

contract HealthDRS is Ownable {

    struct Key {
        uint8 depth;
        bool shareable;
        bool tradeable;
        bool salable;
        bytes32 parent;
        address owner;
    }

    struct SalesOffer {
        address buyer;
        uint price;
    }

    mapping (bytes32 => string) urls;
    mapping(bytes32 => Key) keys;
    mapping(bytes32 => bytes32) public keyData;
    mapping(bytes32 => address[]) public sharedOwners;
    mapping(address => bytes32[]) public accountKeys;
    mapping(bytes32 => SalesOffer) public salesOffers;
    mapping(bytes32 => bytes32) public tradeOffers;

    StandardToken public token;
    address public latestContract = address(this);
    uint8 public version = 1;

    event KeyCreated(address indexed _owner, bytes32 indexed _key);
    event KeySold(bytes32 _key, address indexed _seller, address indexed _buyer, uint _price);
    event KeysTraded(bytes32 indexed _key1, bytes32 indexed _key2);
    event KeyMoved(bytes32 indexed _key, bytes32 _from, bytes32 _to);
    event Access(address indexed _owner, bytes32 indexed _from, bytes32 indexed _to, uint _time, string _data);
    event Message(address indexed _owner, bytes32 indexed _from, bytes32 indexed _to, uint _time, string _category, string _data);
    event Log(address indexed _owner, bytes32 indexed _from, uint _time, string _data);

    modifier ownsKey(bytes32 key) {
        require(isOwner(key, msg.sender));
        _;
    }

    modifier validKey(bytes32 key) {
      require(keys[key].owner != address(0));
      _;
    }

    modifier canSell(bytes32 key) {
      require(keys[key].salable);
      _;
    }

    modifier canTrade(bytes32 key) {
      require(keys[key].tradeable);
      _;
    }

    modifier canShare(bytes32 key) {
      require(keys[key].shareable);
      _;
    }

    /**
    * URLs
    * used to refrence gatekeeper services
    * which use these keys to permission access
    */

    function getURL(bytes32 key)
        public
        constant
        validKey(key)
        returns (string)
    {
        if (keys[key].depth == 0) {
            return urls[key];
        } else {
           return getURL(keys[key].parent);
        }
    }

    //owners of root keys can update the url
    function updateURL(bytes32 key, string url) public ownsKey(key) {
       require(keys[key].depth == 0); //root key only
       urls[key] = url;
    }

    //to purchase keys a user must authorize this contract to spend HLTH
    function authorizedToSpend() public constant returns (uint) {
        return token.allowance(msg.sender, address(this));
    }

    //allow owner access to tokens erroneously transfered to this contract
    function recoverTokens(StandardToken _token, uint amount) public onlyOwner {
        _token.transfer(owner, amount);
    }

    function setHealthCashToken(StandardToken _token) public onlyOwner {
        token = _token;
    }

    function setLatestContract(address _contract) public onlyOwner {
        latestContract = _contract;
    }

    function createKey(string url) public {

        bytes32 id = keccak256(url,now,msg.sender);

        //do not recreate keys
        require(keys[id].owner == address(0));
        keys[id].owner = msg.sender;
        keys[id].depth = 0;
        keys[id].shareable = true;
        keys[id].tradeable = true;
        keys[id].salable = true;
        urls[id] = url; //save the url

        accountKeys[msg.sender].push(id);
        KeyCreated(msg.sender, id);
    }

    function createChildKey(bytes32 key)
        public
        ownsKey(key)
        returns (bytes32 id)
    {
        //depth limited to 100
        require(keys[key].depth < 100);

        //create a unique id
        id = keccak256(key,now,msg.sender);

        //do not recreate keys
        require(keys[id].owner == address(0));
        keys[id].owner = msg.sender;
        keys[id].depth = keys[key].depth + 1;
        keys[id].parent = key;

        accountKeys[msg.sender].push(id);
        KeyCreated(msg.sender, id);
    }

    function shareKey(bytes32 key, address account)
        public
        ownsKey(key)
        canShare(key)
    {
        if (isOwner(key, account) == false) {
            sharedOwners[key].push(account);
            accountKeys[account].push(key);
        }
    }

    function unShareKey(bytes32 key, address account)
        public
        ownsKey(key)
    {
        if (keys[key].owner != account) {
            bool foundKey = false;
            for (uint i = 0; i < sharedOwners[key].length; i++) {
                if (sharedOwners[key][i] == account) {
                    foundKey = true;
                    break;
                }
            }
            if (foundKey) {
                if (i != sharedOwners[key].length - 1) {
                    sharedOwners[key][i] = sharedOwners[key][sharedOwners[key].length - 1];
                }
                sharedOwners[key].length--;
                removeFromAccountKeys(account, key);
            }
        }
    }

    /**
    * Sell Keys
    */
    function createSalesOffer(bytes32 key, address buyer, uint price)
        public
        ownsKey(key)
        canSell(key)
    {
        //cancell trade offer & create sales offer
        tradeOffers[key] = bytes32(0);
        salesOffers[key].buyer = buyer;
        salesOffers[key].price = price;
    }

    function cancelSalesOffer(bytes32 key)
        public
        ownsKey(key)
        canSell(key)
    {
        salesOffers[key].buyer = address(0);
        salesOffers[key].price = 0;
    }

    function purchaseKey(bytes32 key, uint value)
        public
        canSell(key)
    {

       //require explictit authority to spend tokens on the purchasers behalf
       require(value <= authorizedToSpend());
       require(salesOffers[key].buyer == msg.sender);
       require(salesOffers[key].price == value);

       //price is in HLTH tokens
       assert(token.transferFrom(msg.sender, keys[key].owner, value));

       KeySold(key, keys[key].owner, msg.sender, value);

       removeFromAccountKeys(keys[key].owner, key);
       accountKeys[msg.sender].push(key);
       keys[key].owner = msg.sender;

       //key is no longer for sale
       salesOffers[key].buyer = 0;
       salesOffers[key].price = 0;
    }

    /**
    * Trade Keys
    */
    function tradeKey(bytes32 have, bytes32 want)
        public
        ownsKey(have)
        canTrade(have)
        canTrade(want)
    {
       if (tradeOffers[want] == have) {

           KeysTraded(want, have);
           tradeOffers[want] = ""; //remove the tradeOffer

           //complete the trade
           removeFromAccountKeys(keys[have].owner, have);
           removeFromAccountKeys(keys[want].owner, want);
           accountKeys[keys[want].owner].push(have);
           accountKeys[msg.sender].push(want);

           keys[have].owner = keys[want].owner;
           keys[want].owner = msg.sender;

        } else {
            //create a trade offer & cancel sales offer
            cancelSalesOffer(have);
            tradeOffers[have] = want;
        }
    }

    /**
    * Manage Keys
    * manage, update, and inspect keys
    */

    function getKey(bytes32 key)
        public
        constant
        validKey(key)
        returns(address, uint8, bool, bool, bool, bytes32)
    {
       return (keys[key].owner,
               keys[key].depth,
               keys[key].shareable,
               keys[key].tradeable,
               keys[key].salable,
               keys[key].parent);
    }

    //only allow ancestor to grant/deny permission they have
    function setKeyPermissions(
        bytes32 ancestor,
        bytes32 descendant,
        bool shareable,
        bool tradeable,
        bool salable)
        public
        ownsKey(ancestor)
        validKey(descendant)
    {
        require(isAncestor(ancestor, descendant));

        if (keys[ancestor].shareable) {
            keys[descendant].shareable = shareable;
            //unshare if shared
            if (shareable == false) {
                for (uint i = 0; i < sharedOwners[descendant].length; i++) {
                    removeFromAccountKeys(sharedOwners[descendant][i], descendant);
                }
                sharedOwners[descendant].length = 0;
            }
        }

        if (keys[ancestor].tradeable) {
            keys[descendant].tradeable = tradeable;
            //cancel any existing trades
            if (tradeable == false) {
                tradeOffers[descendant] = "";
            }
        }

        if (keys[ancestor].salable) {
            keys[descendant].salable = salable;
            //cancel any existing sales
            if (salable == false) {
                salesOffers[descendant].buyer = 0;
                salesOffers[descendant].price = 0;
            }
        }

    }

    function isOwner(bytes32 key, address account)
        public
        constant
        validKey(key)
        returns (bool)
    {
        bool owns = false;
        if (keys[key].owner == account) {
            owns = true;
        }
        if (owns == false && keys[key].shareable) {
            for (uint i = 0; i < sharedOwners[key].length; i++) {
                if (sharedOwners[key][i] == account) {
                    owns = true;
                    break;
                }
           }
        }

        return owns;
    }

    /* Recursive implementation limits the
       ancestry dept to around 100, we impose this
       limit in the createChild function to avoid
       this.
    */
    function isAncestor(bytes32 ancestor, bytes32 key)
        public
        constant
        validKey(ancestor)
        validKey(key)
        returns (bool)
    {
        if (keys[key].depth == 0) {
            return false;
        } else if (keys[key].parent == ancestor) {
            return true;
        } else {
            return isAncestor(ancestor, keys[key].parent);
        }
    }

    function moveKey(bytes32 ancestor, bytes32 key, bytes32 destination)
        public
        ownsKey(ancestor)
        ownsKey(destination)
    {
        require(isAncestor(ancestor, key));
        keys[key].parent = destination;
    }

    function getAncestorCount(bytes32 key)
        public
        constant
        validKey(key)
        returns(uint)
    {
       return keys[key].depth;
    }

    function getAncestor(bytes32 key, uint depth)
        public
        constant
        validKey(key)
        returns (bytes32)
    {
        require(depth <= getAncestorCount(key));

        if (keys[key].depth == depth) {
            return key;
        } else {
            return getAncestor(keys[key].parent, depth);
        }
    }

    function removeFromAccountKeys(address account, bytes32 key) private {

       bool foundKey = false;

       for (uint i = 0; i < accountKeys[account].length; i++) {
           if (accountKeys[account][i] == key) {
               foundKey = true;
               break;
           }
       }
       if (foundKey) {
           if (i != accountKeys[account].length - 1) {
               accountKeys[account][i] = accountKeys[account][accountKeys[account].length - 1];
           }
           accountKeys[account].length--;
       }
    }

    /**
    * Key Data
    * data, such as permissions, can be stored
    * on the blockchain for any of your descendant keys
    */

    function getKeyData(bytes32 ancestor, bytes32 child, bytes32 dataKey)
        public
        constant
        ownsKey(ancestor)
        validKey(child)
        returns (bytes32)
    {
        require(isAncestor(ancestor, child));
        return keyData[keccak256(child,dataKey)];
    }

    function setKeyData(bytes32 ancestor, bytes32 child, bytes32 dataKey, bytes32 dataValue)
        public
        ownsKey(ancestor)
        validKey(child)
    {
        require(isAncestor(ancestor, child));
        keyData[keccak256(child,dataKey)] = dataValue;
    }

    /**
    * ECRecover
    */
    function recoverAddress(
        bytes32 msgHash,
        uint8 v,
        bytes32 r,
        bytes32 s)
        constant
        returns (address)
    {
      return ecrecover(msgHash, v, r, s);
    }

    /**
    * Logging & Messaging
    * functions for creating an auditable record of activity
    */
    function logAccess(bytes32 from, bytes32 to, string data)
        public
        ownsKey(from)
        validKey(to)
    {
        require(isAncestor(from, to));
        Access(msg.sender, from, to, now, data);
    }

    function message(bytes32 from, bytes32 to, string category, string data)
        public
        ownsKey(from)
        validKey(to)
    {
        Message(msg.sender, from, to, now, category, data);
    }

    function log(bytes32 from, string data)
        public
        ownsKey(from)
    {
        Log(msg.sender, from, now, data);
    }

}

pragma solidity 0.4.15;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'zeppelin-solidity/contracts/token/StandardToken.sol';

/**
* Health Decentralized Record Service (DRS)
* This contract enables creation of url/key sets which can
* be managed, shared, traded, and sold using Health Cash (HLTH).
*
* These url/key sets enable data gatekeeper services and
* cryptographically secure data exchanges.
*/

contract HealthDRS is Ownable {

    struct Key {
        address owner;
        uint8 depth;
        bool shareable;
        bool tradeable;
        bool salable;
        bytes32 parent;
        mapping(bytes32 => bytes32) data;
    }

    struct SalesOffer {
        address buyer;
        uint price;
    }

    mapping (bytes32 => string) urls;
    mapping(bytes32 => Key) keys;
    mapping(bytes32 => address[]) sharedOwners;
    mapping(address => bytes32[]) public accountKeys;
    mapping(bytes32 => SalesOffer) public salesOffers;
    mapping(bytes32 => bytes32) public tradeOffers;
    address public latestContract = address(this);
    StandardToken public token;
    uint16 public version = 1;

    event KeyCreated(address indexed _owner, bytes32 indexed _key);
    event KeySold(bytes32 _key, address indexed _seller, address indexed _buyer, uint _price);
    event KeysTraded(bytes32 indexed _key1, bytes32 indexed _key2);
    event KeyMoved(bytes32 indexed _key, bytes32 _from, bytes32 _to);
    event Access(address indexed _owner, bytes32 indexed _from, bytes32 indexed _to, uint _time, string _data);
    event Message(address indexed _owner, bytes32 indexed _from, bytes32 indexed _to, uint _time, string _category, string _data);
    event Log(address indexed _owner, bytes32 indexed _from, uint _time, string _data);

    modifier ownsKey(bytes32 key) {
        require(isOwner(key, msg.sender));
        _;
    }

    modifier validKey(bytes32 key) {
      require(keys[key].owner != address(0));
      _;
    }

    modifier canSell(bytes32 key) {
      require(keys[key].salable);
      _;
    }

    modifier canTrade(bytes32 key) {
      require(keys[key].tradeable);
      _;
    }

    modifier canShare(bytes32 key) {
      require(keys[key].shareable);
      _;
    }

    /**
    * URLs
    * used to refrence gatekeeper services
    * which use these keys to permission access
    */

    function getURL(bytes32 key)
        public
        constant
        validKey(key)
        returns (string)
    {
        if (keys[key].depth == 0) {
            return urls[key];
        } else {
           return getURL(keys[key].parent);
        }
    }

    //owners of root keys can update the url
    function updateURL(bytes32 key, string url) public ownsKey(key) {
       require(keys[key].depth == 0); //root key only
       urls[key] = url;
    }

    //to purchase keys a user must authorize this contract to spend HLTH
    function authorizedToSpend() public constant returns (uint) {
        return token.allowance(msg.sender, address(this));
    }

    //allow owner access to tokens erroneously transfered to this contract
    function recoverTokens(StandardToken _token, uint amount) public onlyOwner {
        _token.transfer(owner, amount);
    }

    function setHealthCashToken(StandardToken _token) public onlyOwner {
        token = _token;
    }

    function setLatestContract(address _contract) public onlyOwner {
        latestContract = _contract;
    }

    function createKey(string url) public {

        bytes32 id = keccak256(url,now,msg.sender);

        //do not recreate keys
        require(keys[id].owner == address(0));
        keys[id].owner = msg.sender;
        keys[id].depth = 0;
        keys[id].shareable = true;
        keys[id].tradeable = true;
        keys[id].salable = true;
        urls[id] = url; //save the url

        accountKeys[msg.sender].push(id);
        KeyCreated(msg.sender, id);
    }

    function createChildKey(bytes32 key)
        public
        ownsKey(key)
        returns (bytes32 id)
    {
        //depth limited to 100
        require(keys[key].depth < 100);

        //create a unique id
        id = keccak256(key,now,msg.sender);

        //do not recreate keys
        require(keys[id].owner == address(0));
        keys[id].owner = msg.sender;
        keys[id].depth = keys[key].depth + 1;
        keys[id].parent = key;

        accountKeys[msg.sender].push(id);
        KeyCreated(msg.sender, id);
    }

    function shareKey(bytes32 key, address account)
        public
        ownsKey(key)
        canShare(key)
    {
        if (isOwner(key, account) == false) {
            sharedOwners[key].push(account);
            accountKeys[account].push(key);
        }
    }

    function unShareKey(bytes32 key, address account)
        public
        ownsKey(key)
    {
        if (keys[key].owner != account) {
            bool foundKey = false;
            for (uint i = 0; i < sharedOwners[key].length; i++) {
                if (sharedOwners[key][i] == account) {
                    foundKey = true;
                    break;
                }
            }
            if (foundKey) {
                if (i != sharedOwners[key].length - 1) {
                    sharedOwners[key][i] = sharedOwners[key][sharedOwners[key].length - 1];
                }
                sharedOwners[key].length--;
                removeFromAccountKeys(account, key);
            }
        }
    }

    /**
    * Sell Keys
    */
    function createSalesOffer(bytes32 key, address buyer, uint price)
        public
        ownsKey(key)
        canSell(key)
    {
        //cancell trade offer & create sales offer
        tradeOffers[key] = bytes32(0);
        salesOffers[key].buyer = buyer;
        salesOffers[key].price = price;
    }

    function cancelSalesOffer(bytes32 key)
        public
        ownsKey(key)
        canSell(key)
    {
        salesOffers[key].buyer = address(0);
        salesOffers[key].price = 0;
    }

    function purchaseKey(bytes32 key, uint value)
        public
        canSell(key)
    {

       //require explictit authority to spend tokens on the purchasers behalf
       require(value <= authorizedToSpend());
       require(salesOffers[key].buyer == msg.sender);
       require(salesOffers[key].price == value);

       //price is in HLTH tokens
       assert(token.transferFrom(msg.sender, keys[key].owner, value));

       KeySold(key, keys[key].owner, msg.sender, value);

       removeFromAccountKeys(keys[key].owner, key);
       accountKeys[msg.sender].push(key);
       keys[key].owner = msg.sender;

       //key is no longer for sale
       salesOffers[key].buyer = 0;
       salesOffers[key].price = 0;
    }

    /**
    * Trade Keys
    */
    function tradeKey(bytes32 have, bytes32 want)
        public
        ownsKey(have)
        canTrade(have)
        canTrade(want)
    {
       if (tradeOffers[want] == have) {

           KeysTraded(want, have);
           tradeOffers[want] = ""; //remove the tradeOffer

           //complete the trade
           removeFromAccountKeys(keys[have].owner, have);
           removeFromAccountKeys(keys[want].owner, want);
           accountKeys[keys[want].owner].push(have);
           accountKeys[msg.sender].push(want);

           keys[have].owner = keys[want].owner;
           keys[want].owner = msg.sender;

        } else {
            //create a trade offer & cancel sales offer
            cancelSalesOffer(have);
            tradeOffers[have] = want;
        }
    }

    /**
    * Manage Keys
    * manage, update, and inspect keys
    */

    function getKey(bytes32 key)
        public
        constant
        validKey(key)
        returns(address, uint8, bool, bool, bool, bytes32)
    {
       return (keys[key].owner,
               keys[key].depth,
               keys[key].shareable,
               keys[key].tradeable,
               keys[key].salable,
               keys[key].parent);
    }

    //only allow ancestor to grant/deny permission they have
    function setKeyPermissions(
        bytes32 ancestor,
        bytes32 descendant,
        bool shareable,
        bool tradeable,
        bool salable)
        public
        ownsKey(ancestor)
        validKey(descendant)
    {
        require(isAncestor(ancestor, descendant));

        if (keys[ancestor].shareable) {
            keys[descendant].shareable = shareable;
            //unshare if shared
            if (shareable == false) {
                for (uint i = 0; i < sharedOwners[descendant].length; i++) {
                    removeFromAccountKeys(sharedOwners[descendant][i], descendant);
                }
                sharedOwners[descendant].length = 0;
            }
        }

        if (keys[ancestor].tradeable) {
            keys[descendant].tradeable = tradeable;
            //cancel any existing trades
            if (tradeable == false) {
                tradeOffers[descendant] = "";
            }
        }

        if (keys[ancestor].salable) {
            keys[descendant].salable = salable;
            //cancel any existing sales
            if (salable == false) {
                salesOffers[descendant].buyer = 0;
                salesOffers[descendant].price = 0;
            }
        }

    }

    function isOwner(bytes32 key, address account)
        public
        constant
        validKey(key)
        returns (bool)
    {
        bool owns = false;
        if (keys[key].owner == account) {
            owns = true;
        }
        if (owns == false && keys[key].shareable) {
            for (uint i = 0; i < sharedOwners[key].length; i++) {
                if (sharedOwners[key][i] == account) {
                    owns = true;
                    break;
                }
           }
        }

        return owns;
    }

    /* Recursive implementation limits the
       ancestry dept to around 100, we impose this
       limit in the createChild function to avoid
       this.
    */
    function isAncestor(bytes32 ancestor, bytes32 key)
        public
        constant
        validKey(ancestor)
        validKey(key)
        returns (bool)
    {
        if (keys[key].depth == 0) {
            return false;
        } else if (keys[key].parent == ancestor) {
            return true;
        } else {
            return isAncestor(ancestor, keys[key].parent);
        }
    }

    function moveKey(bytes32 ancestor, bytes32 key, bytes32 destination)
        public
        ownsKey(ancestor)
        ownsKey(destination)
    {
        require(isAncestor(ancestor, key));
        keys[key].parent = destination;
    }

    function getAncestorCount(bytes32 key)
        public
        constant
        validKey(key)
        returns(uint)
    {
       return keys[key].depth;
    }

    function getAncestor(bytes32 key, uint depth)
        public
        constant
        validKey(key)
        returns (bytes32)
    {
        require(depth <= getAncestorCount(key));

        if (keys[key].depth == depth) {
            return key;
        } else {
            return getAncestor(keys[key].parent, depth);
        }
    }

    function removeFromAccountKeys(address account, bytes32 key) private {

       bool foundKey = false;

       for (uint i = 0; i < accountKeys[account].length; i++) {
           if (accountKeys[account][i] == key) {
               foundKey = true;
               break;
           }
       }
       if (foundKey) {
           if (i != accountKeys[account].length - 1) {
               accountKeys[account][i] = accountKeys[account][accountKeys[account].length - 1];
           }
           accountKeys[account].length--;
       }
    }

    /**
    * Key Data
    * data, such as permissions, can be stored
    * on the blockchain for any of your descendant keys
    */

    function getKeyData(bytes32 ancestor, bytes32 child, bytes32 dataKey)
        public
        constant
        ownsKey(ancestor)
        returns (bytes32)
    {
        require(isAncestor(ancestor, child));
        return keys[child].data[dataKey];
    }

    function setKeyData(bytes32 ancestor, bytes32 child, bytes32 dataKey, bytes32 dataValue)
        public
        ownsKey(ancestor)
    {
        require(isAncestor(ancestor, child));
        keys[child].data[dataKey] = dataValue;
    }

    /**
    * ECRecover
    */
    function recoverAddress(
        bytes32 msgHash,
        uint8 v,
        bytes32 r,
        bytes32 s)
        constant
        returns (address)
    {
      return ecrecover(msgHash, v, r, s);
    }

    /**
    * Logging & Messaging
    * functions for creating an auditable record of activity
    */
    function logAccess(bytes32 from, bytes32 to, string data)
        public
        ownsKey(from)
        validKey(to)
    {
        require(isAncestor(from, to));
        Access(msg.sender, from, to, now, data);
    }

    function message(bytes32 from, bytes32 to, string category, string data)
        public
        ownsKey(from)
        validKey(to)
    {
        Message(msg.sender, from, to, now, category, data);
    }

    function log(bytes32 from, string data)
        public
        ownsKey(from)
    {
        Log(msg.sender, from, now, data);
    }

}


pragma solidity 0.4.15;

import './ownership/multiowned.sol';
import './crowdsale/FixedTimeBonuses.sol';
import './crowdsale/FundsRegistry.sol';
import './STQToken.sol';
import 'zeppelin-solidity/contracts/ReentrancyGuard.sol';
import 'zeppelin-solidity/contracts/math/Math.sol';
import 'zeppelin-solidity/contracts/math/SafeMath.sol';

/// @title Storiqa ICO contract
contract STQCrowdsale is multiowned, ReentrancyGuard {
    using Math for uint256;
    using SafeMath for uint256;
    using FixedTimeBonuses for FixedTimeBonuses.Data;

    uint internal constant MSK2UTC_DELTA = 3600 * 3;

    enum IcoState { INIT, ICO, PAUSED, FAILED, SUCCEEDED }

    event StateChanged(IcoState indexed _state);

    modifier requiresState(IcoState _state) {
        require(m_state == _state);
        _;
    }

    /// @dev triggers some state changes based on current time
    modifier timedStateChange() {
        if (IcoState.INIT == m_state && now >= getStartTime())
            changeState(IcoState.ICO);
        if (IcoState.ICO == m_state && now > getEndTime())
            finishICO();

        _;
    }

    // PUBLIC interface

    function STQCrowdsale(address[] _owners)
        multiowned(_owners, 2)
    {
        require(3 == _owners.length);
        m_token = new STQToken(_owners);
        m_funds = new FundsRegistry(_owners, 2, this);

        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505768399 + MSK2UTC_DELTA, bonus: 25}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505941199 + MSK2UTC_DELTA, bonus: 20}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506200399 + MSK2UTC_DELTA, bonus: 15}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506545999 + MSK2UTC_DELTA, bonus: 10}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506891599 + MSK2UTC_DELTA, bonus: 5}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1508360399 + MSK2UTC_DELTA, bonus: 0}));
        m_bonuses.validate(true);
    }

    // PUBLIC interface: payments

    // fallback function as a shortcut
    function() payable {
        buy();  // only internal call here!
    }

    /// @notice ICO participation
    /// @return number of STQ tokens bought (with all decimal symbols)
    function buy()
        public
        payable
        timedStateChange
        requiresState(IcoState.ICO)
        nonReentrant
        returns (uint)
    {
        // automatic check for unaccounted withdrawals
        if (maybeAutoPause()) {
            changeState(IcoState.PAUSED);
            msg.sender.transfer(msg.value);     // we cant throw (have to save state), so refunding this way
            return 0;
        }

        address investor = msg.sender;
        uint256 payment = msg.value;
        require(payment > 0);

        uint startingInvariant = this.balance.add(m_funds.balance);

        // checking for max cap
        uint fundsAllowed = c_MaximumFunds.sub(m_funds.totalInvested());
        assert(0 != fundsAllowed);  // in this case state must not be IcoState.ICO
        payment = fundsAllowed.min256(payment);
        uint256 change = msg.value.sub(payment);

        // issue tokens
        uint stq = calcSTQAmount(payment);
        m_token.mint(investor, stq);

        // record payment
        m_funds.invested.value(payment)(investor);

        // check if ICO must be closed early
        if (change > 0)
        {
            assert(c_MaximumFunds == m_funds.totalInvested());
            finishICO();

            // send change
            investor.transfer(change);
            assert(startingInvariant == this.balance.add(m_funds.balance).sub(change));
        }
        else
            assert(startingInvariant == this.balance.add(m_funds.balance));

        return stq;
    }

    // PUBLIC interface: owners: maintenance

    /// @notice pauses ICO
    function pause()
        external
        timedStateChange
        requiresState(IcoState.ICO)
        onlyowner
    {
        changeState(IcoState.PAUSED);
    }

    /// @notice resume paused ICO
    function unpause()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.ICO);
        checkTime();
    }

    /// @notice consider paused ICO as failed
    function fail()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.FAILED);
    }

    /// @notice In case we need to attach to existent token
    function setToken(address _token)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(0x0 != _token);
        m_token = STQToken(_token);
    }

    /// @notice In case we need to attach to existent funds
    function setFundsRegistry(address _funds)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(0x0 != _funds);
        m_funds = FundsRegistry(_funds);
    }

    /// @notice explicit trigger for timed state changes
    function checkTime()
        public
        timedStateChange
        onlyowner
    {
    }

    // INTERNAL functions

    function finishICO() private {
        if (m_funds.totalInvested() < c_MinFunds)
            changeState(IcoState.FAILED);
        else
            changeState(IcoState.SUCCEEDED);
    }

    /// @dev performs only allowed state transitions
    function changeState(IcoState _newState) private {
        assert(m_state != _newState);

        if (IcoState.INIT == m_state) {        assert(IcoState.ICO == _newState); }
        else if (IcoState.ICO == m_state) {    assert(IcoState.PAUSED == _newState || IcoState.FAILED == _newState || IcoState.SUCCEEDED == _newState); }
        else if (IcoState.PAUSED == m_state) { assert(IcoState.ICO == _newState || IcoState.FAILED == _newState); }
        else assert(false);

        m_state = _newState;
        // this should be tightly linked
        if (IcoState.SUCCEEDED == m_state) {
            onSuccess();
        } else if (IcoState.FAILED == m_state) {
            onFailure();
        }

        StateChanged(m_state);
    }

    function onSuccess() private {
        // mint tokens for owners
        uint tokensPerOwner = m_token.totalSupply().mul(4).div(m_numOwners);
        for (uint i = 0; i < m_numOwners; i++)
            m_token.mint(getOwner(i), tokensPerOwner);

        m_funds.changeState(FundsRegistry.State.SUCCEEDED);
        m_token.startCirculation();
    }

    function onFailure() private {
        m_funds.changeState(FundsRegistry.State.REFUNDING);
    }

    /// @dev automatic check for unaccounted withdrawals
    function maybeAutoPause() private returns (bool) {
        if (IcoState.SUCCEEDED == m_state || IcoState.FAILED == m_state)
            return false;   // expecting withdrawals

        if (m_funds.balance < m_lastFundsAmount)
            return true;

        m_lastFundsAmount = m_funds.balance;
        return false;
    }

    /// @dev calculates amount of STQ to which payer of _wei is entitled
    function calcSTQAmount(uint _wei) private constant returns (uint) {
        uint stq = _wei.mul(c_STQperETH);

        // apply bonus
        stq = stq.mul(m_bonuses.getBonus(now).add(100)).div(100);

        return stq;
    }

    /// @dev start time of the ICO, inclusive
    function getStartTime() private constant returns (uint) {
        return c_startTime;
    }

    /// @dev end time of the ICO, inclusive
    function getEndTime() private constant returns (uint) {
        return m_bonuses.getLastTime();
    }

    // FIELDS

    /// @notice starting exchange rate of STQ
    uint public constant c_STQperETH = 100;

    /// @notice minimum investments to consider ICO as a success
    uint public constant c_MinFunds = 3000 ether;

    /// @notice maximum investments to be accepted during ICO
    uint public constant c_MaximumFunds = 550000 ether;

    /// @notice start time of the ICO
    uint public constant c_startTime = 1505682000 + MSK2UTC_DELTA;

    /// @notice timed bonuses
    FixedTimeBonuses.Data m_bonuses;

    /// @dev state of the ICO
    IcoState public m_state = IcoState.INIT;

    /// @dev contract responsible for token accounting
    STQToken public m_token;

    /// @dev contract responsible for investments accounting
    FundsRegistry public m_funds;

    /// @dev last recorded funds
    uint256 public m_lastFundsAmount;
}

pragma solidity 0.4.15;

import './ownership/multiowned.sol';
import './crowdsale/FixedTimeBonuses.sol';
import './crowdsale/FundsRegistry.sol';
import './STQToken.sol';
import 'zeppelin-solidity/contracts/ReentrancyGuard.sol';
import 'zeppelin-solidity/contracts/math/Math.sol';
import 'zeppelin-solidity/contracts/math/SafeMath.sol';

/// @title Storiqa ICO contract
contract STQCrowdsale is multiowned, ReentrancyGuard {
    using Math for uint256;
    using SafeMath for uint256;
    using FixedTimeBonuses for FixedTimeBonuses.Data;

    uint internal constant MSK2UTC_DELTA = 3600 * 3;

    enum IcoState { INIT, ICO, PAUSED, FAILED, SUCCEEDED }

    event StateChanged(IcoState indexed _state);

    modifier requiresState(IcoState _state) {
        require(m_state == _state);
        _;
    }

    /// @dev triggers some state changes based on current time
    modifier timedStateChange() {
        if (IcoState.INIT == m_state && now >= getStartTime())
            changeState(IcoState.ICO);
        if (IcoState.ICO == m_state && now > getEndTime())
            finishICO();

        _;
    }

    // PUBLIC interface

    function STQCrowdsale(address[] _owners)
        multiowned(_owners, 2)
    {
        require(3 == _owners.length);
        m_token = new STQToken(_owners);
        m_funds = new FundsRegistry(_owners, 2, this);

        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505768399 + MSK2UTC_DELTA, bonus: 25}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505941199 + MSK2UTC_DELTA, bonus: 20}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506200399 + MSK2UTC_DELTA, bonus: 15}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506545999 + MSK2UTC_DELTA, bonus: 10}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506891599 + MSK2UTC_DELTA, bonus: 5}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1508360399 + MSK2UTC_DELTA, bonus: 0}));
        m_bonuses.validate(true);
    }

    // PUBLIC interface: payments

    // fallback function as a shortcut
    function() payable {
        buy();  // only internal call here!
    }

    /// @notice ICO participation
    /// @return number of STQ tokens bought (with all decimal symbols)
    function buy()
        public
        payable
        timedStateChange
        requiresState(IcoState.ICO)
        nonReentrant
        returns (uint)
    {
        address investor = msg.sender;
        uint256 payment = msg.value;
        require(payment > 0);

        uint startingInvariant = this.balance.add(m_funds.balance);

        // checking for max cap
        uint fundsAllowed = c_MaximumFunds.sub(m_funds.totalInvested());
        assert(0 != fundsAllowed);  // in this case state must not be IcoState.ICO
        payment = fundsAllowed.min256(payment);
        uint256 change = msg.value.sub(payment);

        // issue tokens
        uint stq = calcSTQAmount(payment);
        m_token.mint(investor, stq);

        // record payment
        m_funds.invested.value(payment)(investor);

        // check if ICO must be closed early
        if (change > 0)
        {
            assert(c_MaximumFunds == m_funds.totalInvested());
            finishICO();

            // send change
            investor.transfer(change);
            assert(startingInvariant == this.balance.add(m_funds.balance).sub(change));
        }
        else
            assert(startingInvariant == this.balance.add(m_funds.balance));

        return stq;
    }

    // PUBLIC interface: owners: maintenance

    /// @notice pauses ICO
    function pause()
        external
        timedStateChange
        requiresState(IcoState.ICO)
        onlyowner
    {
        changeState(IcoState.PAUSED);
    }

    /// @notice resume paused ICO
    function unpause()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.ICO);
        checkTime();
    }

    /// @notice consider paused ICO as failed
    function fail()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.FAILED);
    }

    /// @notice In case we need to attach to existent token
    function setToken(address _token)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(0x0 != _token);
        m_token = STQToken(_token);
    }

    /// @notice In case we need to attach to existent funds
    function setFundsRegistry(address _funds)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(0x0 != _funds);
        m_funds = FundsRegistry(_funds);
    }

    /// @notice explicit trigger for timed state changes
    function checkTime()
        public
        timedStateChange
        onlyowner
    {
    }

    // INTERNAL functions

    function finishICO() private {
        if (m_funds.totalInvested() < c_MinFunds)
            changeState(IcoState.FAILED);
        else
            changeState(IcoState.SUCCEEDED);
    }

    /// @dev performs only allowed state transitions
    function changeState(IcoState _newState) private {
        assert(m_state != _newState);

        if (IcoState.INIT == m_state) {        assert(IcoState.ICO == _newState); }
        else if (IcoState.ICO == m_state) {    assert(IcoState.PAUSED == _newState || IcoState.FAILED == _newState || IcoState.SUCCEEDED == _newState); }
        else if (IcoState.PAUSED == m_state) { assert(IcoState.ICO == _newState || IcoState.FAILED == _newState); }
        else assert(false);

        m_state = _newState;
        // this should be tightly linked
        if (IcoState.SUCCEEDED == m_state) {
            onSuccess();
        } else if (IcoState.FAILED == m_state) {
            onFailure();
        }

        StateChanged(m_state);
    }

    function onSuccess() private {
        // mint tokens for owners
        uint tokensPerOwner = m_token.totalSupply().mul(4).div(m_numOwners);
        for (uint i = 0; i < m_numOwners; i++)
            m_token.mint(getOwner(i), tokensPerOwner);

        m_funds.changeState(FundsRegistry.State.SUCCEEDED);
        m_token.startCirculation();
    }

    function onFailure() private {
        m_funds.changeState(FundsRegistry.State.REFUNDING);
    }

    /// @dev calculates amount of STQ to which payer of _wei is entitled
    function calcSTQAmount(uint _wei) private constant returns (uint) {
        uint stq = _wei.mul(c_STQperETH);

        // apply bonus
        stq = stq.mul(m_bonuses.getBonus(now).add(100)).div(100);

        return stq;
    }

    /// @dev start time of the ICO, inclusive
    function getStartTime() private constant returns (uint) {
        return c_startTime;
    }

    /// @dev end time of the ICO, inclusive
    function getEndTime() private constant returns (uint) {
        return m_bonuses.getLastTime();
    }

    // FIELDS

    /// @notice starting exchange rate of STQ
    uint public constant c_STQperETH = 100;

    /// @notice minimum investments to consider ICO as a success
    uint public constant c_MinFunds = 3000 ether;

    /// @notice maximum investments to be accepted during ICO
    uint public constant c_MaximumFunds = 550000 ether;

    /// @notice start time of the ICO
    uint public constant c_startTime = 1505682000 + MSK2UTC_DELTA;

    /// @notice timed bonuses
    FixedTimeBonuses.Data m_bonuses;

    /// @dev state of the ICO
    IcoState public m_state = IcoState.INIT;

    /// @dev contract responsible for token accounting
    STQToken public m_token;

    /// @dev contract responsible for investments accounting
    FundsRegistry public m_funds;
}


update userRegistry

update userRegistry

import "concept.sol";
import "assessment.sol";
import "user.sol";
import "conceptRegistry.sol";

/*
@type: contract
@name: UserRegistry
@purpose: To store data about users for easy and secure access
*/
contract UserRegistry
{
  address public conceptRegistryAddress; //The address of the conceptRegistry contract
  mapping (address => uint)  balance; //Maps the addresses of users to their token balances
  mapping (address => address[])  history; //Maps the addresses of users to an array of addresses that contain the addresses of their assessments
  mapping (address => bool)  availability; //Maps the addresses of users to their availability status for whether or not they can currently assess someone
  mapping (address => address)  users; //Maps the addresses of the users to their account
  bool firstUserMade = false; //Keeps track of whether or not the first user has been made yet
  event UserCreation(address _userAddress); //address of the created user contract

  /*
  @type: modifier
  @name: onlyConcept
  @purpose: to only allow the Concept contract to call a function to which this modifier is applied
  */
  modifier onlyConcept()
  {
    if(ConceptRegistry(conceptRegistryAddress).conceptExists(msg.sender) == false) //checks if the address calling the function is not a concept
    {
      throw; //throws out the fucntion call
    }
    _;
  }

  /*
  @type: modifier
  @name: onlyThis
  @purpose: to only allow the this contract to call a function to which this modifier is applied
  */
  modifier onlyThis()
  {
    if(msg.sender != address(this)) //Checks if msg.sender is this contract
    {
      throw; //Throws out the function call if it isn't
    }
    _;
  }

  /*
  @type: constructor function
  @purpose: To initialize the userMaster contract
  @param: address creator = the address of the creator contract
  @returns: nothing
  */
  function UserRegistry(address conceptRegistry)
  {
    conceptRegistryAddress = conceptRegistry; //Sets the address of the conceptRegistry contract
  }

  /*
  @type: function
  @purpose: To create a user contract
  @param: address userAddress = the address of the user's wallet
  @param: address masterAddress = the address of the master contract that stores this user's address
  @returns: nothing
  */
  function addUser(address userAddress)
  {
    User newUser = new User(userAddress, address(this), conceptRegistryAddress); //Makes a new user that represents the address from userAddress and uses the master from masterAddress as its datastore
    Concept(ConceptRegistry(conceptRegistryAddress).mewAddress()).addUser(address(newUser));
    UserCreation(address(newUser)); //Makes a new UserCreation event with the address of the newly created user
  }

  function firstUser(address userAddress)
  {
    if(firstUserMade == false)
    {
      User newUser = new User(userAddress, address(this), conceptRegistryAddress); //Makes a new user that represents the address from userAddress and uses the master from masterAddress as its datastore
      Concept(ConceptRegistry(conceptRegistryAddress).mewAddress()).addUser(address(newUser));
      UserCreation(address(newUser)); //Makes a new UserCreation event with the address of the newly created user
      balance[address(newUser)] = 1000;
    }
  }

  /*
  @type: function
  @purpose: To map the address of a user to the address of the concept that the user has just passed
  @param: address user = the address of the user
  @param: address assessment = the address of the assessment completed
  @returns: nothing
  */
  function mapHistory(address user, address assessment) onlyConcept()
  {
    history[user].push(assessment); //adds the address of the concept to the end of the array that is mapped to the user
  }

  /*
  @type: function
  @purpose: To map the user's availability to assess to the user's address
  @param: address user = the address of the user
  @param: bool available = the availa status of the user to assess
  @returns: nothing
  */
  function mapAvailability(bool available)
  {
    availability[msg.sender] = available; //sets the availability of the user specified in the parameter to the availability specified in the parameter
  }

  /*
  @type: function
  @purpose: To set a new token balance for a user
  @param: address user = the address of the user whose token balance is to be mapped
  @param: uint balance = the new token balance for the user
  @returns: nothing
  */
  function mapBalance(address user, uint newBalance) onlyConcept()
  {
    balance[user] = newBalance; //sets the token balance of the user
  }

  /*
  @type: function
  @purpose: To get the user's token balance
  @param: address user = the address of the user
  @returns: The token balance in the form of a uint
  */
  function getBalance(address user) constant returns(uint)
  {
    return balance[user];
  }

  /*
  @type: function
  @purpose: To get the addresses of the assessments that the user has completed
  @param: address user = the address of the user
  @returns: The addresses of the assessments that the user has completed in the form of an array of addresses
  */
  function getHistory(address user) constant returns(address[])
  {
    return history[user];
  }

  /*
  @type: function
  @purpose: To get the number of assessments completed by a user
  @param: address user = the address of the user
  @returns: The number of assessments that a user has completed in the form of a uint
  */
  function getNumberOfHistory(address user) constant returns(uint)
  {
    return history[user].length;
  }

  /*
  @type: function
  @purpose: To get the availability of a user to assess
  @param: address user = the address of the user
  @returns: The availability of a user to assess in the form of a bool
  */
  function getAvailability(address user) constant returns(bool)
  {
    return availability[user];
  }

  function transfer(address user, uint amount) returns(bool)
  {
    if(balance[msg.sender] > amount)
    {
      balance[msg.sender] -= amount;
      balance[user] += amount;
      return true;
    }
    else
    {
      return false;
    }
  }

  /*
  @type: function
  @purpose: to remove this contract
  @param: address receiver = the address of the wallet that will receive of the ether
  @returns: nothing
  */
  function remove(address reciever) onlyThis()
  {
    suicide(reciever);
  }
}

import "concept.sol";
import "assessment.sol";
import "user.sol";
import "conceptMaster.sol";

/*
@type: contract
@name: UserMaster
@purpose: To store data about users for easy and secure access
*/
contract UserMaster
{
  address public conceptMasterAddress; //The address of the conceptMaster contract
  mapping (address => uint)  balance; //Maps the addresses of users to their token balances
  mapping (address => address[])  history; //Maps the addresses of users to an array of addresses that contain the addresses of their assessments
  mapping (address => bool)  availability; //Maps the addresses of users to their availability status for whether or not they can currently assess someone
  mapping (address => address)  users; //Maps the addresses of the users to their account
  bool firstUserMade = false; //Keeps track of whether or not the first user has been made yet
  event UserCreation(address _userAddress); //address of the created user contract

  /*
  @type: modifier
  @name: onlyConcept
  @purpose: to only allow the Concept contract to call a function to which this modifier is applied
  */
  modifier onlyConcept()
  {
    if(ConceptMaster(conceptMasterAddress).conceptExists(msg.sender) == false) //checks if the address calling the function is not a concept
    {
      throw; //throws out the fucntion call
    }
    _;
  }

  /*
  @type: modifier
  @name: onlyThis
  @purpose: to only allow the this contract to call a function to which this modifier is applied
  */
  modifier onlyThis()
  {
    if(msg.sender != address(this)) //Checks if msg.sender is this contract
    {
      throw; //Throws out the function call if it isn't
    }
    _;
  }

  /*
  @type: constructor function
  @purpose: To initialize the userMaster contract
  @param: address creator = the address of the creator contract
  @returns: nothing
  */
  function UserMaster(address conceptMaster)
  {
    conceptMasterAddress = conceptMaster; //Sets the address of the conceptMaster contract
  }

  /*
  @type: function
  @purpose: To create a user contract
  @param: address userAddress = the address of the user's wallet
  @param: address masterAddress = the address of the master contract that stores this user's address
  @returns: nothing
  */
  function addUser(address userAddress)
  {
    User newUser = new User(userAddress, address(this), conceptMasterAddress); //Makes a new user that represents the address from userAddress and uses the master from masterAddress as its datastore
    Concept(ConceptMaster(conceptMasterAddress).mewAddress()).addUser(address(newUser));
    UserCreation(address(newUser)); //Makes a new UserCreation event with the address of the newly created user
  }

  function firstUser(address userAddress)
  {
    if(firstUserMade == false)
    {
      User newUser = new User(userAddress, address(this), conceptMasterAddress); //Makes a new user that represents the address from userAddress and uses the master from masterAddress as its datastore
      Concept(ConceptMaster(conceptMasterAddress).mewAddress()).addUser(address(newUser));
      UserCreation(address(newUser)); //Makes a new UserCreation event with the address of the newly created user
      balance[address(newUser)] = 1000;
    }
  }

  /*
  @type: function
  @purpose: To map the address of a user to the address of the concept that the user has just passed
  @param: address user = the address of the user
  @param: address assessment = the address of the assessment completed
  @returns: nothing
  */
  function mapHistory(address user, address assessment) onlyConcept()
  {
    history[user].push(assessment); //adds the address of the concept to the end of the array that is mapped to the user
  }

  /*
  @type: function
  @purpose: To map the user's availability to assess to the user's address
  @param: address user = the address of the user
  @param: bool available = the availa status of the user to assess
  @returns: nothing
  */
  function mapAvailability(bool available)
  {
    availability[msg.sender] = available; //sets the availability of the user specified in the parameter to the availability specified in the parameter
  }

  /*
  @type: function
  @purpose: To set a new token balance for a user
  @param: address user = the address of the user whose token balance is to be mapped
  @param: uint balance = the new token balance for the user
  @returns: nothing
  */
  function mapBalance(address user, uint newBalance) onlyConcept()
  {
    balance[user] = newBalance; //sets the token balance of the user
  }

  /*
  @type: function
  @purpose: To get the user's token balance
  @param: address user = the address of the user
  @returns: The token balance in the form of a uint
  */
  function getBalance(address user) constant returns(uint)
  {
    return balance[user];
  }

  /*
  @type: function
  @purpose: To get the addresses of the assessments that the user has completed
  @param: address user = the address of the user
  @returns: The addresses of the assessments that the user has completed in the form of an array of addresses
  */
  function getHistory(address user) constant returns(address[])
  {
    return history[user];
  }

  /*
  @type: function
  @purpose: To get the number of assessments completed by a user
  @param: address user = the address of the user
  @returns: The number of assessments that a user has completed in the form of a uint
  */
  function getNumberOfHistory(address user) constant returns(uint)
  {
    return history[user].length;
  }

  /*
  @type: function
  @purpose: To get the availability of a user to assess
  @param: address user = the address of the user
  @returns: The availability of a user to assess in the form of a bool
  */
  function getAvailability(address user) constant returns(bool)
  {
    return availability[user];
  }

  function transfer(address user, uint amount) returns(bool)
  {
    if(balance[msg.sender] > amount)
    {
      balance[msg.sender] -= amount;
      balance[user] += amount;
      return true;
    }
    else
    {
      return false;
    }
  }

  /*
  @type: function
  @purpose: to remove this contract
  @param: address receiver = the address of the wallet that will receive of the ether
  @returns: nothing
  */
  function remove(address reciever) onlyThis()
  {
    suicide(reciever);
  }
}


Preserve camelCase in GovernorCountingSimple (#3608)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorCountingSimple.sol)

pragma solidity ^0.8.0;

import "../Governor.sol";

/**
 * @dev Extension of {Governor} for simple, 3 options, vote counting.
 *
 * _Available since v4.3._
 */
abstract contract GovernorCountingSimple is Governor {
    /**
     * @dev Supported vote types. Matches Governor Bravo ordering.
     */
    enum VoteType {
        Against,
        For,
        Abstain
    }

    struct ProposalVote {
        uint256 againstVotes;
        uint256 forVotes;
        uint256 abstainVotes;
        mapping(address => bool) hasVoted;
    }

    mapping(uint256 => ProposalVote) private _proposalVotes;

    /**
     * @dev See {IGovernor-COUNTING_MODE}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function COUNTING_MODE() public pure virtual override returns (string memory) {
        return "support=bravo&quorum=for,abstain";
    }

    /**
     * @dev See {IGovernor-hasVoted}.
     */
    function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
        return _proposalVotes[proposalId].hasVoted[account];
    }

    /**
     * @dev Accessor to the internal vote counts.
     */
    function proposalVotes(uint256 proposalId)
        public
        view
        virtual
        returns (
            uint256 againstVotes,
            uint256 forVotes,
            uint256 abstainVotes
        )
    {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];
        return (proposalVote.againstVotes, proposalVote.forVotes, proposalVote.abstainVotes);
    }

    /**
     * @dev See {Governor-_quorumReached}.
     */
    function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];

        return quorum(proposalSnapshot(proposalId)) <= proposalVote.forVotes + proposalVote.abstainVotes;
    }

    /**
     * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes.
     */
    function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];

        return proposalVote.forVotes > proposalVote.againstVotes;
    }

    /**
     * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo).
     */
    function _countVote(
        uint256 proposalId,
        address account,
        uint8 support,
        uint256 weight,
        bytes memory // params
    ) internal virtual override {
        ProposalVote storage proposalVote = _proposalVotes[proposalId];

        require(!proposalVote.hasVoted[account], "GovernorVotingSimple: vote already cast");
        proposalVote.hasVoted[account] = true;

        if (support == uint8(VoteType.Against)) {
            proposalVote.againstVotes += weight;
        } else if (support == uint8(VoteType.For)) {
            proposalVote.forVotes += weight;
        } else if (support == uint8(VoteType.Abstain)) {
            proposalVote.abstainVotes += weight;
        } else {
            revert("GovernorVotingSimple: invalid value for enum VoteType");
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (governance/extensions/GovernorCountingSimple.sol)

pragma solidity ^0.8.0;

import "../Governor.sol";

/**
 * @dev Extension of {Governor} for simple, 3 options, vote counting.
 *
 * _Available since v4.3._
 */
abstract contract GovernorCountingSimple is Governor {
    /**
     * @dev Supported vote types. Matches Governor Bravo ordering.
     */
    enum VoteType {
        Against,
        For,
        Abstain
    }

    struct ProposalVote {
        uint256 againstVotes;
        uint256 forVotes;
        uint256 abstainVotes;
        mapping(address => bool) hasVoted;
    }

    mapping(uint256 => ProposalVote) private _proposalVotes;

    /**
     * @dev See {IGovernor-COUNTING_MODE}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function COUNTING_MODE() public pure virtual override returns (string memory) {
        return "support=bravo&quorum=for,abstain";
    }

    /**
     * @dev See {IGovernor-hasVoted}.
     */
    function hasVoted(uint256 proposalId, address account) public view virtual override returns (bool) {
        return _proposalVotes[proposalId].hasVoted[account];
    }

    /**
     * @dev Accessor to the internal vote counts.
     */
    function proposalVotes(uint256 proposalId)
        public
        view
        virtual
        returns (
            uint256 againstVotes,
            uint256 forVotes,
            uint256 abstainVotes
        )
    {
        ProposalVote storage proposalvote = _proposalVotes[proposalId];
        return (proposalvote.againstVotes, proposalvote.forVotes, proposalvote.abstainVotes);
    }

    /**
     * @dev See {Governor-_quorumReached}.
     */
    function _quorumReached(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalvote = _proposalVotes[proposalId];

        return quorum(proposalSnapshot(proposalId)) <= proposalvote.forVotes + proposalvote.abstainVotes;
    }

    /**
     * @dev See {Governor-_voteSucceeded}. In this module, the forVotes must be strictly over the againstVotes.
     */
    function _voteSucceeded(uint256 proposalId) internal view virtual override returns (bool) {
        ProposalVote storage proposalvote = _proposalVotes[proposalId];

        return proposalvote.forVotes > proposalvote.againstVotes;
    }

    /**
     * @dev See {Governor-_countVote}. In this module, the support follows the `VoteType` enum (from Governor Bravo).
     */
    function _countVote(
        uint256 proposalId,
        address account,
        uint8 support,
        uint256 weight,
        bytes memory // params
    ) internal virtual override {
        ProposalVote storage proposalvote = _proposalVotes[proposalId];

        require(!proposalvote.hasVoted[account], "GovernorVotingSimple: vote already cast");
        proposalvote.hasVoted[account] = true;

        if (support == uint8(VoteType.Against)) {
            proposalvote.againstVotes += weight;
        } else if (support == uint8(VoteType.For)) {
            proposalvote.forVotes += weight;
        } else if (support == uint8(VoteType.Abstain)) {
            proposalvote.abstainVotes += weight;
        } else {
            revert("GovernorVotingSimple: invalid value for enum VoteType");
        }
    }
}


Just tidied up a comment

Just tidied up a comment

pragma solidity ^0.4.2;

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

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

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

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

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

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

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

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

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

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

contract TestERC20Token is ERC20Interface {

    // Owner of this contract
    address public owner;

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

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

    // Total supply
    uint256 _totalSupply;

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

    // Constructor
    function TestERC20Token() {
        owner = msg.sender;
    }

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.4.2;

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

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

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

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

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

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

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

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

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

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

contract TestERC20Token is ERC20Interface {

    // Owner of this contract
    address public owner;

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

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

    // Total supply
    uint256 _totalSupply;

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

    // Constructor
    function TestERC20Token() {
        owner = msg.sender;
    }

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

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

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

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

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

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

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

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

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

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

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


call event on close()

call event on close()

/* Copyright (C) 2017 ethbets
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD license. See the LICENSE file for details.
*/

pragma solidity ^0.4.11;

import './ERC20.sol';
import './governanceInterface.sol';

contract Bet is ProposalInterface {

  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
  modifier onlyArbiter() {
    require(msg.sender == address(arbiter));
    _;
  }
  modifier beforeTimestamp(uint timestamp) {
    require(block.timestamp < timestamp);
    _;
  }
  modifier afterTimestamp(uint timestamp) {
    require(block.timestamp >= timestamp);
    _;
  }
  modifier matchIsOpenOrUndecided() {
    require(betState == BET_STATES.OPEN || betState == BET_STATES.UNDECIDED);
    _;
  }
  modifier matchIsDecided() {
    require(betState == BET_STATES.TEAM_ZERO_WON ||
            betState == BET_STATES.TEAM_ONE_WON ||
            betState == BET_STATES.DRAW);
    _;
  }

  enum BET_STATES {
    OPEN,
    TEAM_ZERO_WON,
    TEAM_ONE_WON,
    DRAW,
    UNDECIDED,
    CALLED_RESOLVER
  }

  address public owner; // Can be a parent contract
  GovernanceInterface public arbiter; // Governance account

  // Bet data
  BET_STATES public betState = BET_STATES.OPEN;
  bool public isFeatured;
  string public team0Name;
  string public team1Name;
  uint public team0BetSum;
  uint public team1BetSum;
  mapping (address => uint) public betsToTeam0;
  mapping (address => uint) public betsToTeam1;

  // ERC20 support
  mapping (address => mapping (address => uint)) public ERC20BetsToTeam0;
  mapping (address => mapping (address => uint)) public ERC20BetsToTeam1;
  mapping (address => uint) public ERC20Team0BetSum;
  mapping (address => uint) public ERC20Team1BetSum;
  address[] public validERC20;

  // Chronology data
  uint public timestampMatchBegin;
  uint public timestampMatchEnd;
  uint public timestampArbiterDeadline;
  // Self-destruct is possible if time > timestampSelfDestructDeadline
  uint public timestampSelfDestructDeadline;

  uint8 public TAX;
  uint constant TIMESTAMP_MARGIN = 1000;

  event NewBet(bool forTeam, address indexed from, uint amount);
  event NewBetERC20(bool forTeam, address indexed from, uint amount, address erc20);
  event StateChanged(BET_STATES state);

  function Bet(GovernanceInterface _arbiter, string _team0Name,
               string _team1Name, uint[] _timestamps, uint8 _tax
               ) {
    require(block.timestamp < _timestamps[0]);
    require(_timestamps[0] < _timestamps[1]);
    require(_timestamps[1] < _timestamps[2]);
    require(_timestamps[2] < _timestamps[3]);

    isFeatured = false;
    owner = msg.sender;
    arbiter = _arbiter;
    team0Name = _team0Name;
    team1Name = _team1Name;
    // TODO: PUT BACK TIMESTAMP_MARGIN SUMS, PUT REQUIRES!
    timestampMatchBegin = _timestamps[0];// - TIMESTAMP_MARGIN;
    timestampMatchEnd = _timestamps[1];// + TIMESTAMP_MARGIN;
    timestampArbiterDeadline = _timestamps[2];// + TIMESTAMP_MARGIN;
    timestampSelfDestructDeadline = _timestamps[3];// + TIMESTAMP_MARGIN;
    TAX = _tax;
  }

  function __resolve(uint outcome)
    onlyArbiter()
    afterTimestamp(timestampMatchEnd)
    beforeTimestamp(timestampArbiterDeadline) {
    require(betState == BET_STATES.CALLED_RESOLVER);
    if (outcome == 1)
      betState = BET_STATES.TEAM_ZERO_WON;
    else if (outcome == 2)
      betState = BET_STATES.TEAM_ONE_WON;
    else if (outcome == 3)
      betState = BET_STATES.DRAW;
    else
      betState = BET_STATES.UNDECIDED;
    StateChanged(betState);
  }

  // Will create a Proposal on the arbiter
  function updateResult() payable
    matchIsOpenOrUndecided()
    afterTimestamp(timestampMatchEnd) {
    require(betState != BET_STATES.CALLED_RESOLVER);
    betState = BET_STATES.CALLED_RESOLVER;
    StateChanged(betState);
    arbiter.addProposal(this, timestampArbiterDeadline);
  }

  function toggleFeatured() onlyOwner() {
    isFeatured = !isFeatured;
  }

  function bet(bool forTeam) payable
    beforeTimestamp(timestampMatchBegin) {
    require(!arbiter.isMember(msg.sender));
    require(msg.value > 0);
    uint prevSum;

    if (forTeam == false) {
      // Cannot bet in two teams
      require(betsToTeam1[msg.sender] == 0);
      prevSum = team0BetSum;
      require((prevSum + msg.value) >= prevSum); // Overflow
      team0BetSum += msg.value;
      assert(team0BetSum >= prevSum);
      betsToTeam0[msg.sender] += msg.value;
    }
    else {
      // Cannot bet in two teams
      require(betsToTeam0[msg.sender] == 0);
      prevSum = team1BetSum;
      require((prevSum + msg.value) >= prevSum); // Overflow
      team1BetSum += msg.value;
      assert(team1BetSum >= prevSum);
      betsToTeam1[msg.sender] += msg.value;
    }

    NewBet(forTeam, msg.sender, msg.value);
  }

  function checkAddERC20(address erc20) internal {
    if (ERC20Team0BetSum[erc20] == 0 && ERC20Team1BetSum[erc20] == 0) {
      validERC20.push(erc20);
    }
  }

  function betERC20(address erc20, bool forTeam, uint amount)
    beforeTimestamp(timestampMatchBegin) {
    require(!arbiter.isMember(msg.sender));
    require(amount > 0);
    uint prevSum;

    ERC20 erc20Contract = ERC20(erc20);
    require(erc20Contract.transferFrom(msg.sender, this, amount));

    checkAddERC20(erc20);

    if (forTeam == false) {
      // Cannot bet in two teams
      require(ERC20BetsToTeam1[erc20][msg.sender] == 0);
      prevSum = ERC20Team0BetSum[erc20];
      require((prevSum + amount) >= prevSum); // Overflow
      ERC20Team0BetSum[erc20] += amount;
      assert(ERC20Team0BetSum[erc20] >= prevSum);
      ERC20BetsToTeam0[erc20][msg.sender] += amount;
    }
    else {
      // Cannot bet in two teams
      require(ERC20BetsToTeam0[erc20][msg.sender] == 0);
      prevSum = ERC20Team1BetSum[erc20];
      require((prevSum + amount) >= prevSum); // Overflow
      ERC20Team1BetSum[erc20] += amount;
      assert(ERC20Team1BetSum[erc20] >= prevSum);
      ERC20BetsToTeam1[erc20][msg.sender] += amount;
    }

    NewBetERC20(forTeam, msg.sender, amount, erc20);
  }

  function withdraw(address[] tokens)
    afterTimestamp(timestampMatchEnd)
    matchIsDecided() {
    uint idx;
    if (betState == BET_STATES.DRAW) {
      msg.sender.transfer(collectOriginalBet());
      for (idx = 0; idx < validERC20.length; ++idx) {
        uint amount = collectOriginalBetERC20(validERC20[idx]);
        if (amount > 0) {
          ERC20 erc20Contract = ERC20(validERC20[idx]);
          require(erc20Contract.transfer(msg.sender, amount));
        }
      }
    }
    else {
      collectProfit();
      for (idx = 0; idx < tokens.length; ++idx) {
        collectProfitERC20(tokens[idx]);
      }
    }
  }

  // Transfers the user's initial bet back
  function collectOriginalBet() internal returns(uint) {
    uint amount;
    if (betsToTeam0[msg.sender] > 0) {
      amount = betsToTeam0[msg.sender];
      betsToTeam0[msg.sender] = 0;
      return amount;
    }
    else if (betsToTeam1[msg.sender] > 0) {
      amount = betsToTeam1[msg.sender];
      betsToTeam1[msg.sender] = 0;
      return amount;
    }
    else {
      return 0;
    }
  }

  // Transfers the user's initial bet back
  function collectOriginalBetERC20(address erc20) internal returns(uint) {
    uint amount;
    if (ERC20BetsToTeam0[erc20][msg.sender] > 0) {
      amount = ERC20BetsToTeam0[erc20][msg.sender];
      ERC20BetsToTeam0[erc20][msg.sender] = 0;
      return amount;
    }
    else if (ERC20BetsToTeam1[erc20][msg.sender] > 0) {
      amount = ERC20BetsToTeam1[erc20][msg.sender];
      ERC20BetsToTeam1[erc20][msg.sender] = 0;
      return amount;
    }
    else {
      return 0;
    }
  }

  function collectProfit() internal {
    uint betAmount = 0;
    uint sum = 0;
    uint profit = 0;

    if (betState == BET_STATES.TEAM_ZERO_WON && betsToTeam0[msg.sender] > 0) {
      betAmount = betsToTeam0[msg.sender];
      sum = team0BetSum;
      profit = team1BetSum;
    }
    else if (betState == BET_STATES.TEAM_ONE_WON && betsToTeam1[msg.sender] > 0) {
      betAmount = betsToTeam1[msg.sender];
      sum = team1BetSum;
      profit = team0BetSum;
    }
    else {
      return;
    }

    assert(betAmount <= sum);

    profit = computeProfit(betAmount, sum, profit);
    msg.sender.transfer(profit + collectOriginalBet());
  }

  function collectProfitERC20(address erc20) internal {
    uint betAmount = 0;
    uint sum = 0;
    uint profit = 0;

    if (betState == BET_STATES.TEAM_ZERO_WON && ERC20BetsToTeam0[erc20][msg.sender] > 0) {
      betAmount = ERC20BetsToTeam0[erc20][msg.sender];
      sum = ERC20Team0BetSum[erc20];
      profit = ERC20Team1BetSum[erc20];
    }
    else if (betState == BET_STATES.TEAM_ONE_WON && ERC20BetsToTeam1[erc20][msg.sender] > 0) {
      betAmount = ERC20BetsToTeam1[erc20][msg.sender];
      sum = ERC20Team1BetSum[erc20];
      profit = ERC20Team0BetSum[erc20];
    }
    else {
      return;
    }

    assert(betAmount <= sum);

    profit = computeProfit(betAmount, sum, profit);
    ERC20 erc20Contract = ERC20(erc20);
    require(erc20Contract.transfer(msg.sender, profit + collectOriginalBetERC20(erc20)));
  }

  // Compute the user's profit
  function computeProfit(uint betAmount, uint sum, uint profit) internal returns(uint) {
    // Approach one:
    // We might lose precision, but no overflow
    var senderPc = betAmount / sum;
    assert(senderPc >= 0 && senderPc <= 1);

    var senderProfit = senderPc * profit;
    assert(senderProfit <= profit);

    // Approach two:
    // Better precision, since multiplication is done first, but may overflow
    //uint sender_profit = (bet * profit) / sum;

    var mulTax = (senderProfit * TAX);
    require(mulTax >= senderProfit); // Overflow
    var tax = mulTax / 100;
    assert(tax <= senderProfit);

    var notaxProfit = senderProfit;
    senderProfit -= tax;
    assert(senderProfit <= notaxProfit);

    return senderProfit;

    // We can collect the bet tax by ourselves when the bet self-destructs
    // owner.transfer(tax);
    //msg.sender.transfer(senderProfit + collectOriginalBet());
  }

  /* After the arbiter deadline and before the self-destruct
   * deadline, anyone can set the bet state to DRAW.
   * this is in the unlikely event if the arbiter don't
   * decide in time, every one can collect the funds.
  */
  function close()
    afterTimestamp(timestampArbiterDeadline)
    beforeTimestamp(timestampSelfDestructDeadline) {
    betState = BET_STATES.DRAW;
    StateChanged(betState);
  }

  /* Selfdestructs the bet and return what it has in the account
   * as a fee to the bet's owner.
   */
  function terminate()
    afterTimestamp(timestampSelfDestructDeadline) {
    selfdestruct(owner);
  }

  /* Fallback just throws now
   * Can do something, maybe increase the value of both pools
  */
  function () { require(false); }
}

/* Copyright (C) 2017 ethbets
 * All rights reserved.
 *
 * This software may be modified and distributed under the terms
 * of the BSD license. See the LICENSE file for details.
*/

pragma solidity ^0.4.11;

import './ERC20.sol';
import './governanceInterface.sol';

contract Bet is ProposalInterface {

  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
  modifier onlyArbiter() {
    require(msg.sender == address(arbiter));
    _;
  }
  modifier beforeTimestamp(uint timestamp) {
    require(block.timestamp < timestamp);
    _;
  }
  modifier afterTimestamp(uint timestamp) {
    require(block.timestamp >= timestamp);
    _;
  }
  modifier matchIsOpenOrUndecided() {
    require(betState == BET_STATES.OPEN || betState == BET_STATES.UNDECIDED);
    _;
  }
  modifier matchIsDecided() {
    require(betState == BET_STATES.TEAM_ZERO_WON ||
            betState == BET_STATES.TEAM_ONE_WON ||
            betState == BET_STATES.DRAW);
    _;
  }

  enum BET_STATES {
    OPEN,
    TEAM_ZERO_WON,
    TEAM_ONE_WON,
    DRAW,
    UNDECIDED,
    CALLED_RESOLVER
  }

  address public owner; // Can be a parent contract
  GovernanceInterface public arbiter; // Governance account

  // Bet data
  BET_STATES public betState = BET_STATES.OPEN;
  bool public isFeatured;
  string public team0Name;
  string public team1Name;
  uint public team0BetSum;
  uint public team1BetSum;
  mapping (address => uint) public betsToTeam0;
  mapping (address => uint) public betsToTeam1;

  // ERC20 support
  mapping (address => mapping (address => uint)) public ERC20BetsToTeam0;
  mapping (address => mapping (address => uint)) public ERC20BetsToTeam1;
  mapping (address => uint) public ERC20Team0BetSum;
  mapping (address => uint) public ERC20Team1BetSum;
  address[] public validERC20;

  // Chronology data
  uint public timestampMatchBegin;
  uint public timestampMatchEnd;
  uint public timestampArbiterDeadline;
  // Self-destruct is possible if time > timestampSelfDestructDeadline
  uint public timestampSelfDestructDeadline;

  uint8 public TAX;
  uint constant TIMESTAMP_MARGIN = 1000;

  event NewBet(bool forTeam, address indexed from, uint amount);
  event NewBetERC20(bool forTeam, address indexed from, uint amount, address erc20);
  event StateChanged(BET_STATES state);

  function Bet(GovernanceInterface _arbiter, string _team0Name,
               string _team1Name, uint[] _timestamps, uint8 _tax
               ) {
    require(block.timestamp < _timestamps[0]);
    require(_timestamps[0] < _timestamps[1]);
    require(_timestamps[1] < _timestamps[2]);
    require(_timestamps[2] < _timestamps[3]);

    isFeatured = false;
    owner = msg.sender;
    arbiter = _arbiter;
    team0Name = _team0Name;
    team1Name = _team1Name;
    // TODO: PUT BACK TIMESTAMP_MARGIN SUMS, PUT REQUIRES!
    timestampMatchBegin = _timestamps[0];// - TIMESTAMP_MARGIN;
    timestampMatchEnd = _timestamps[1];// + TIMESTAMP_MARGIN;
    timestampArbiterDeadline = _timestamps[2];// + TIMESTAMP_MARGIN;
    timestampSelfDestructDeadline = _timestamps[3];// + TIMESTAMP_MARGIN;
    TAX = _tax;
  }

  function __resolve(uint outcome)
    onlyArbiter()
    afterTimestamp(timestampMatchEnd)
    beforeTimestamp(timestampArbiterDeadline) {
    require(betState == BET_STATES.CALLED_RESOLVER);
    if (outcome == 1)
      betState = BET_STATES.TEAM_ZERO_WON;
    else if (outcome == 2)
      betState = BET_STATES.TEAM_ONE_WON;
    else if (outcome == 3)
      betState = BET_STATES.DRAW;
    else
      betState = BET_STATES.UNDECIDED;
    StateChanged(betState);
  }

  // Will create a Proposal on the arbiter
  function updateResult() payable
    matchIsOpenOrUndecided()
    afterTimestamp(timestampMatchEnd) {
    require(betState != BET_STATES.CALLED_RESOLVER);
    betState = BET_STATES.CALLED_RESOLVER;
    StateChanged(betState);
    arbiter.addProposal(this, timestampArbiterDeadline);
  }

  function toggleFeatured() onlyOwner() {
    isFeatured = !isFeatured;
  }

  function bet(bool forTeam) payable
    beforeTimestamp(timestampMatchBegin) {
    require(!arbiter.isMember(msg.sender));
    require(msg.value > 0);
    uint prevSum;

    if (forTeam == false) {
      // Cannot bet in two teams
      require(betsToTeam1[msg.sender] == 0);
      prevSum = team0BetSum;
      require((prevSum + msg.value) >= prevSum); // Overflow
      team0BetSum += msg.value;
      assert(team0BetSum >= prevSum);
      betsToTeam0[msg.sender] += msg.value;
    }
    else {
      // Cannot bet in two teams
      require(betsToTeam0[msg.sender] == 0);
      prevSum = team1BetSum;
      require((prevSum + msg.value) >= prevSum); // Overflow
      team1BetSum += msg.value;
      assert(team1BetSum >= prevSum);
      betsToTeam1[msg.sender] += msg.value;
    }

    NewBet(forTeam, msg.sender, msg.value);
  }

  function checkAddERC20(address erc20) internal {
    if (ERC20Team0BetSum[erc20] == 0 && ERC20Team1BetSum[erc20] == 0) {
      validERC20.push(erc20);
    }
  }

  function betERC20(address erc20, bool forTeam, uint amount)
    beforeTimestamp(timestampMatchBegin) {
    require(!arbiter.isMember(msg.sender));
    require(amount > 0);
    uint prevSum;

    ERC20 erc20Contract = ERC20(erc20);
    require(erc20Contract.transferFrom(msg.sender, this, amount));

    checkAddERC20(erc20);

    if (forTeam == false) {
      // Cannot bet in two teams
      require(ERC20BetsToTeam1[erc20][msg.sender] == 0);
      prevSum = ERC20Team0BetSum[erc20];
      require((prevSum + amount) >= prevSum); // Overflow
      ERC20Team0BetSum[erc20] += amount;
      assert(ERC20Team0BetSum[erc20] >= prevSum);
      ERC20BetsToTeam0[erc20][msg.sender] += amount;
    }
    else {
      // Cannot bet in two teams
      require(ERC20BetsToTeam0[erc20][msg.sender] == 0);
      prevSum = ERC20Team1BetSum[erc20];
      require((prevSum + amount) >= prevSum); // Overflow
      ERC20Team1BetSum[erc20] += amount;
      assert(ERC20Team1BetSum[erc20] >= prevSum);
      ERC20BetsToTeam1[erc20][msg.sender] += amount;
    }

    NewBetERC20(forTeam, msg.sender, amount, erc20);
  }

  function withdraw(address[] tokens)
    afterTimestamp(timestampMatchEnd)
    matchIsDecided() {
    uint idx;
    if (betState == BET_STATES.DRAW) {
      msg.sender.transfer(collectOriginalBet());
      for (idx = 0; idx < validERC20.length; ++idx) {
        uint amount = collectOriginalBetERC20(validERC20[idx]);
        if (amount > 0) {
          ERC20 erc20Contract = ERC20(validERC20[idx]);
          require(erc20Contract.transfer(msg.sender, amount));
        }
      }
    }
    else {
      collectProfit();
      for (idx = 0; idx < tokens.length; ++idx) {
        collectProfitERC20(tokens[idx]);
      }
    }
  }

  // Transfers the user's initial bet back
  function collectOriginalBet() internal returns(uint) {
    uint amount;
    if (betsToTeam0[msg.sender] > 0) {
      amount = betsToTeam0[msg.sender];
      betsToTeam0[msg.sender] = 0;
      return amount;
    }
    else if (betsToTeam1[msg.sender] > 0) {
      amount = betsToTeam1[msg.sender];
      betsToTeam1[msg.sender] = 0;
      return amount;
    }
    else {
      return 0;
    }
  }

  // Transfers the user's initial bet back
  function collectOriginalBetERC20(address erc20) internal returns(uint) {
    uint amount;
    if (ERC20BetsToTeam0[erc20][msg.sender] > 0) {
      amount = ERC20BetsToTeam0[erc20][msg.sender];
      ERC20BetsToTeam0[erc20][msg.sender] = 0;
      return amount;
    }
    else if (ERC20BetsToTeam1[erc20][msg.sender] > 0) {
      amount = ERC20BetsToTeam1[erc20][msg.sender];
      ERC20BetsToTeam1[erc20][msg.sender] = 0;
      return amount;
    }
    else {
      return 0;
    }
  }

  function collectProfit() internal {
    uint betAmount = 0;
    uint sum = 0;
    uint profit = 0;

    if (betState == BET_STATES.TEAM_ZERO_WON && betsToTeam0[msg.sender] > 0) {
      betAmount = betsToTeam0[msg.sender];
      sum = team0BetSum;
      profit = team1BetSum;
    }
    else if (betState == BET_STATES.TEAM_ONE_WON && betsToTeam1[msg.sender] > 0) {
      betAmount = betsToTeam1[msg.sender];
      sum = team1BetSum;
      profit = team0BetSum;
    }
    else {
      return;
    }

    assert(betAmount <= sum);

    profit = computeProfit(betAmount, sum, profit);
    msg.sender.transfer(profit + collectOriginalBet());
  }

  function collectProfitERC20(address erc20) internal {
    uint betAmount = 0;
    uint sum = 0;
    uint profit = 0;

    if (betState == BET_STATES.TEAM_ZERO_WON && ERC20BetsToTeam0[erc20][msg.sender] > 0) {
      betAmount = ERC20BetsToTeam0[erc20][msg.sender];
      sum = ERC20Team0BetSum[erc20];
      profit = ERC20Team1BetSum[erc20];
    }
    else if (betState == BET_STATES.TEAM_ONE_WON && ERC20BetsToTeam1[erc20][msg.sender] > 0) {
      betAmount = ERC20BetsToTeam1[erc20][msg.sender];
      sum = ERC20Team1BetSum[erc20];
      profit = ERC20Team0BetSum[erc20];
    }
    else {
      return;
    }

    assert(betAmount <= sum);

    profit = computeProfit(betAmount, sum, profit);
    ERC20 erc20Contract = ERC20(erc20);
    require(erc20Contract.transfer(msg.sender, profit + collectOriginalBetERC20(erc20)));
  }

  // Compute the user's profit
  function computeProfit(uint betAmount, uint sum, uint profit) internal returns(uint) {
    // Approach one:
    // We might lose precision, but no overflow
    var senderPc = betAmount / sum;
    assert(senderPc >= 0 && senderPc <= 1);

    var senderProfit = senderPc * profit;
    assert(senderProfit <= profit);

    // Approach two:
    // Better precision, since multiplication is done first, but may overflow
    //uint sender_profit = (bet * profit) / sum;

    var mulTax = (senderProfit * TAX);
    require(mulTax >= senderProfit); // Overflow
    var tax = mulTax / 100;
    assert(tax <= senderProfit);

    var notaxProfit = senderProfit;
    senderProfit -= tax;
    assert(senderProfit <= notaxProfit);

    return senderProfit;

    // We can collect the bet tax by ourselves when the bet self-destructs
    // owner.transfer(tax);
    //msg.sender.transfer(senderProfit + collectOriginalBet());
  }

  /* After the arbiter deadline and before the self-destruct
   * deadline, anyone can set the bet state to DRAW.
   * this is in the unlikely event if the arbiter don't
   * decide in time, every one can collect the funds.
  */
  function close()
    afterTimestamp(timestampArbiterDeadline)
    beforeTimestamp(timestampSelfDestructDeadline) {
    betState = BET_STATES.DRAW;
  }

  /* Selfdestructs the bet and return what it has in the account
   * as a fee to the bet's owner.
   */
  function terminate()
    afterTimestamp(timestampSelfDestructDeadline) {
    selfdestruct(owner);
  }

  /* Fallback just throws now
   * Can do something, maybe increase the value of both pools
  */
  function () { require(false); }
}