Mortality removed, all incoming ether will be sent to the fee receiver address

/**
 * Copyright 2016 Everex https://everex.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* String utility library */
library strUtils {
    string constant CHAINY_JSON_ID = '"id":"CHAINY"';
    uint8 constant CHAINY_JSON_MIN_LEN = 32;

    /* Converts given number to base58, limited by _maxLength symbols */
    function toBase58(uint256 _value, uint8 _maxLength) internal returns (string) {
        string memory letters = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
        bytes memory alphabet = bytes(letters);
        uint8 base = 58;
        uint8 len = 0;
        uint256 remainder = 0;
        bool needBreak = false;
        bytes memory bytesReversed = bytes(new string(_maxLength));

        for (uint8 i = 0; i < _maxLength; i++) {
            if(_value < base){
                needBreak = true;
            }
            remainder = _value % base;
            _value = uint256(_value / base);
            bytesReversed[i] = alphabet[remainder];
            len++;
            if(needBreak){
                break;
            }
        }

        // Reverse
        bytes memory result = bytes(new string(len));
        for (i = 0; i < len; i++) {
            result[i] = bytesReversed[len - i - 1];
        }
        return string(result);
    }

    /* Concatenates two strings */
    function concat(string _s1, string _s2) internal returns (string) {
        bytes memory bs1 = bytes(_s1);
        bytes memory bs2 = bytes(_s2);
        string memory s3 = new string(bs1.length + bs2.length);
        bytes memory bs3 = bytes(s3);

        uint256 j = 0;
        for (uint256 i = 0; i < bs1.length; i++) {
            bs3[j++] = bs1[i];
        }
        for (i = 0; i < bs2.length; i++) {
            bs3[j++] = bs2[i];
        }

        return string(bs3);
    }

    /* Checks if provided JSON string has valid Chainy format */
    function isValidChainyJson(string _json) internal returns (bool) {
        bytes memory json = bytes(_json);
        bytes memory id = bytes(CHAINY_JSON_ID);

        if (json.length < CHAINY_JSON_MIN_LEN) {
            return false;
        } else {
            uint len = 0;
            if (json[1] == id[0]) {
                len = 1;
                while (len < id.length && (1 + len) < json.length && json[1 + len] == id[len]) {
                    len++;
                }
                if (len == id.length) {
                    return true;
                }
            }
        }

        return false;
    }
}

// Ownership
contract owned {
    address public owner;

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

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _
    }

    function transferOwnership(address newOwner) onlyOwner {
        owner = newOwner;
    }
}

contract Chainy is owned {
    string constant CHAINY_URL = "https://txn.me/";

    // Configuration
    mapping(string => uint256) private chainyConfig;

    // Service accounts
    mapping (address => bool) private srvAccount;

    // Fee receiver
    address private receiverAddress;

    struct data {uint256 timestamp; string json; address sender;}
    mapping (string => data) private chainy;

    event chainyShortLink(uint256 timestamp, string code);

    // Constructor
    function Chainy(){
        setConfig("fee", 0);
        setConfig("blockoffset", 1000000);
    }

    // Sets configuration option
    function setConfig(string _key, uint256 _value) onlyOwner {
        chainyConfig[_key] = _value;
    }

    // Returns configuration option
    function getConfig(string _key) constant returns (uint256 _value) {
        return chainyConfig[_key];
    }

    // Add/Remove service account
    function setServiceAccount(address _address, bool _value) onlyOwner {
        srvAccount[_address] = _value;
    }

    // Set receiver address
    function setReceiverAddress(address _address) onlyOwner {
        receiverAddress = _address;
    }

    // Add record
    function addChainyData(string json) {
        checkFormat(json);

        var code = generateShortLink();
        // Checks if the record exist
        if (getChainyTimestamp(code) > 0) throw;

        processFee();
        chainy[code] = data({
            timestamp: block.timestamp,
            json: json,
            sender: tx.origin
        });

        // Fire event
        var link = strUtils.concat(CHAINY_URL, code);
        chainyShortLink(block.timestamp, link);
    }

    // Get record timestamp
    function getChainyTimestamp(string code) constant returns (uint256) {
        return chainy[code].timestamp;
    }

    // Get record JSON
    function getChainyData(string code) constant returns (string) {
        return chainy[code].json;
    }

    // Get record sender
    function getChainySender(string code) constant returns (address) {
        return chainy[code].sender;
    }

    // Checks if enough fee provided
    function processFee() internal {
        var fee = getConfig("fee");
        if (srvAccount[msg.sender] || (fee == 0)) return;

        if (msg.value < fee)
            throw;
        else
            if (!receiverAddress.send(msg.value)) throw;
    }

    // Checks if provided string has valid format
    function checkFormat(string json) internal {
        if (!strUtils.isValidChainyJson(json)) throw;
    }

    function generateShortLink() internal returns (string) {
        var s1 = strUtils.toBase58(block.number - getConfig("blockoffset"), 11);
        var s2 = strUtils.toBase58(uint256(tx.origin), 2);

        var s = strUtils.concat(s1, s2);
        return s;
    }

}

/**
 * Copyright 2016 Everex https://everex.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* String utility library */
library strUtils {
    string constant CHAINY_JSON_ID = '"id":"CHAINY"';
    uint8 constant CHAINY_JSON_MIN_LEN = 32;

    /* Converts given number to base58, limited by _maxLength symbols */
    function toBase58(uint256 _value, uint8 _maxLength) internal returns (string) {
        string memory letters = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
        bytes memory alphabet = bytes(letters);
        uint8 base = 58;
        uint8 len = 0;
        uint256 remainder = 0;
        bool needBreak = false;
        bytes memory bytesReversed = bytes(new string(_maxLength));

        for (uint8 i = 0; i < _maxLength; i++) {
            if(_value < base){
                needBreak = true;
            }
            remainder = _value % base;
            _value = uint256(_value / base);
            bytesReversed[i] = alphabet[remainder];
            len++;
            if(needBreak){
                break;
            }
        }

        // Reverse
        bytes memory result = bytes(new string(len));
        for (i = 0; i < len; i++) {
            result[i] = bytesReversed[len - i - 1];
        }
        return string(result);
    }

    /* Concatenates two strings */
    function concat(string _s1, string _s2) internal returns (string) {
        bytes memory bs1 = bytes(_s1);
        bytes memory bs2 = bytes(_s2);
        string memory s3 = new string(bs1.length + bs2.length);
        bytes memory bs3 = bytes(s3);

        uint256 j = 0;
        for (uint256 i = 0; i < bs1.length; i++) {
            bs3[j++] = bs1[i];
        }
        for (i = 0; i < bs2.length; i++) {
            bs3[j++] = bs2[i];
        }

        return string(bs3);
    }

    /* Checks if provided JSON string has valid Chainy format */
    function isValidChainyJson(string _json) internal returns (bool) {
        bytes memory json = bytes(_json);
        bytes memory id = bytes(CHAINY_JSON_ID);

        if (json.length < CHAINY_JSON_MIN_LEN) {
            return false;
        } else {
            uint len = 0;
            if (json[1] == id[0]) {
                len = 1;
                while (len < id.length && (1 + len) < json.length && json[1 + len] == id[len]) {
                    len++;
                }
                if (len == id.length) {
                    return true;
                }
            }
        }

        return false;
    }
}

// Ownership
contract owned {
    address public owner;

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

    modifier onlyOwner {
        if (msg.sender != owner) throw;
        _
    }

    function transferOwnership(address newOwner) onlyOwner {
        owner = newOwner;
    }
}

// Mortality
contract mortal is owned {
    function kill() onlyOwner {
        if (this.balance > 0) {
            if (!msg.sender.send(this.balance)) throw;
        }
        suicide(msg.sender);
    }
}

contract Chainy is owned, mortal {
    string constant CHAINY_URL = "https://txn.me/";

    // Configuration
    mapping(string => uint256) private chainyConfig;

    // Service accounts
    mapping (address => bool) private srvAccount;

    // Fee receiver
    address private receiverAddress;

    struct data {uint256 timestamp; string json; address sender;}
    mapping (string => data) private chainy;

    event chainyShortLink(uint256 timestamp, string code);

    // Constructor
    function Chainy(){
        setConfig("fee", 0);
        setConfig("blockoffset", 1000000);
    }

    // Sets configuration option
    function setConfig(string _key, uint256 _value) onlyOwner {
        chainyConfig[_key] = _value;
    }

    // Returns configuration option
    function getConfig(string _key) constant returns (uint256 _value) {
        return chainyConfig[_key];
    }

    // Add/Remove service account
    function setServiceAccount(address _address, bool _value) onlyOwner {
        srvAccount[_address] = _value;
    }

    // Set receiver address
    function setReceiverAddress(address _address) onlyOwner {
        receiverAddress = _address;
    }

    // Add record
    function addChainyData(string json) {
        checkFormat(json);

        var code = generateShortLink();
        // Checks if the record exist
        if (getChainyTimestamp(code) > 0) throw;

        processFee();
        chainy[code] = data({
            timestamp: block.timestamp,
            json: json,
            sender: tx.origin
        });

        // Fire event
        var link = strUtils.concat(CHAINY_URL, code);
        chainyShortLink(block.timestamp, link);
    }

    // Get record timestamp
    function getChainyTimestamp(string code) constant returns (uint256) {
        return chainy[code].timestamp;
    }

    // Get record JSON
    function getChainyData(string code) constant returns (string) {
        return chainy[code].json;
    }

    // Get record sender
    function getChainySender(string code) constant returns (address) {
        return chainy[code].sender;
    }

    // Checks if enough fee provided
    function processFee() internal {
        var fee = getConfig("fee");
        if (srvAccount[msg.sender] || (fee == 0)) return;

        if (msg.value < fee)
            throw;
        else
            if (!receiverAddress.send(fee)) throw;
    }

    // Checks if provided string has valid format
    function checkFormat(string json) internal {
        if (!strUtils.isValidChainyJson(json)) throw;
    }

    function generateShortLink() internal returns (string) {
        var s1 = strUtils.toBase58(block.number - getConfig("blockoffset"), 11);
        var s2 = strUtils.toBase58(uint256(tx.origin), 2);

        var s = strUtils.concat(s1, s2);
        return s;
    }

}


Add address of created PeerReview contract to DAO

pragma solidity ^0.4.2;

import "PeerReviewContract";

contract PeerReviewDAO {
    address[] peerReviewContracts;

    function createPeerReviewContract(address initJournal, bytes initFileHash) payable {
        // https://solidity.readthedocs.io/en/develop/control-structures.html#creating-contracts-via-new
        // Cannot limit the amount of gas

        PeerReview newPR = (new PeerReview).value(msg.value)(initJournal, initFileHash);
        peerReviewContracts.push(newPR.address);
    }
}

pragma solidity ^0.4.2;

import "PeerReviewContract";

contract PeerReviewDAO {
    address[] peerReviewContracts;

    function createPeerReviewContract(address initJournal, bytes initFileHash) payable {
        // https://solidity.readthedocs.io/en/develop/control-structures.html#creating-contracts-via-new
        // Cannot limit the amount of gas

        PeerReview newPR = (new PeerReview).value(msg.value)(initJournal, initFileHash)
    }
}


AssertGeneral.sol

AssertGeneral.sol

pragma solidity >= 0.4.15 < 0.6.0;

library AssertGeneral {

    /*
        Event: TestEvent

        Fired when an assertion is made.

        Params:
            result (bool) - Whether or not the assertion holds.
            message (string) - A message to display if the assertion does not hold.
    */
    event TestEvent(bool indexed result, string message);

    // ************************************** general **************************************

    /*
        Function: fail()

        Mark the test as failed.

        Params:
            message (string) - A message associated with the failure.

        Returns:
            result (bool) - false.
    */
    function fail(string memory message) public returns (bool result) {
        _report(false, message);
        return false;
    }

    /******************************** internal ********************************/

        /*
            Function: _report

            Internal function for triggering <TestEvent>.

            Params:
                result (bool) - The test result (true or false).
                message (string) - The message that is sent if the assertion fails.
        */
    function _report(bool result, string memory message) internal {
        if(result)
            emit TestEvent(true, "");
        else
            emit TestEvent(false, message);
    }
}

error: pathspec 'packages/truffle-core/lib/testing/AssertGeneral.sol' did not match any file(s) known to git


DelayedPayment.sol fixes

DelayedPayment.sol fixes

pragma solidity ^0.4.21;

import "contracts/Interface/SchedulerInterface.sol";

/// Example of using the Scheduler from a smart contract to delay a payment.
contract DelayedPayment {

    SchedulerInterface public scheduler;

    uint lockedUntil;
    address recipient;
    address public scheduledTransaction;

    function DelayedPayment(
        address _scheduler,
        uint    _numBlocks,
        address _recipient
    )  public payable {
        scheduler = SchedulerInterface(_scheduler);
        lockedUntil = block.number + _numBlocks;
        recipient = _recipient;

        scheduledTransaction = scheduler.schedule.value(0.1 ether)( // 0.1 ether is to pay for gas, bounty and fee
            this,                   // send to self
            "",                     // and trigger fallback function
            [
                200000,             // The amount of gas to be sent with the transaction.
                0,                  // The amount of wei to be sent.
                255,                // The size of the execution window.
                lockedUntil,        // The start of the execution window.
                20000000000 wei,    // The gasprice for the transaction (aka 30 gwei)
                20000000000 wei,    // The fee included in the transaction.
                20000000000 wei,         // The bounty that awards the executor of the transaction.
                30000000000 wei     // The required amount of wei the claimer must send as deposit.
            ]
        );
    }

    function () public payable {
        if (msg.value > 0) { //this handles recieving remaining funds sent while scheduling (0.1 ether)
            return;
        } else if (address(this).balance > 0) {
            payout();
        } else {
            revert();
        }
    }

    function payout()
        public returns (bool)
    {
        require(block.number >= lockedUntil);

        recipient.transfer(address(this).balance);
        return true;
    }
}

pragma solidity ^0.4.21;

import "contracts/Interface/SchedulerInterface.sol";

/// Example of using the Scheduler from a smart contract to delay a payment.
contract DelayedPayment {

    SchedulerInterface public scheduler;

    uint lockedUntil;
    address recipient;

    function DelayedPayment(
        address _scheduler,
        uint    _numBlocks,
        address _recipient
    )  public {
        scheduler = SchedulerInterface(_scheduler);
        lockedUntil = block.number + _numBlocks;
        recipient = _recipient;

        scheduler.schedule.value(2 ether)(
            recipient,              // toAddress
            "",                     // callData
            [
                2000000,            // The amount of gas to be sent with the transaction.
                0,                  // The amount of wei to be sent.
                255,                // The size of the execution window.
                lockedUntil,        // The start of the execution window.
                30000000000 wei,    // The gasprice for the transaction (aka 30 gwei)
                12345 wei,          // The fee included in the transaction.
                224455 wei,         // The bounty that awards the executor of the transaction.
                20000 wei           // The required amount of wei the claimer must send as deposit.
            ]
        );
    }

    function ()  public {
        if (address(this).balance > 0) {
            payout();
        } else {
            revert();
        }
    }

    function payout()
        public returns (bool)
    {
        require(getNow() >= lockedUntil);
        recipient.transfer(address(this).balance);
        return true;
    }

    function getNow()
        internal view returns (uint)
    {
        return block.number;
    }
}


Single lighthouse member bug fixes

Single lighthouse member bug fixes

pragma solidity ^0.4.24;

import './LighthouseAPI.sol';
import './LighthouseABI.sol';
import './LiabilityFactory.sol';

contract LighthouseLib is LighthouseAPI, LighthouseABI {

    function refill(uint256 _value) external {
        require(xrt.transferFrom(msg.sender, this, _value));
        require(_value >= minimalFreeze);

        if (balances[msg.sender] == 0) {
            indexOf[msg.sender] = members.length;
            members.push(msg.sender);
        }
        balances[msg.sender] += _value;
    }

    function withdraw(uint256 _value) external {
        require(balances[msg.sender] >= _value);

        require(xrt.transfer(msg.sender, _value));
        balances[msg.sender] -= _value;

        // Drop member if quota go to zero
        if (quotaOf(msg.sender) == 0) {
            require(xrt.transfer(msg.sender, balances[msg.sender]));
            balances[msg.sender] = 0;

            uint256 senderIndex = indexOf[msg.sender];
            uint256 lastIndex = members.length - 1;
            if (senderIndex < lastIndex)
                members[senderIndex] = members[lastIndex];
            members.length -= 1;
        }
    }

    function nextMember() internal
    { marker = (marker + 1) % members.length; }

    modifier quoted {
        if (quota == 0) {
            // Step over marker
            nextMember();

            // Allocate new quota
            quota = quotaOf(members[marker]);
        }

        // Consume one quota for transaction sending
        quota -= 1;

        _;
    }

    modifier keepalive {
        if (timeoutBlocks < block.number - keepaliveBlock) {
            // Search keepalive sender
            while (msg.sender != members[marker])
                nextMember();

            // Allocate new quota
            quota = quotaOf(members[marker]);
        }

        _;
    }

    modifier member {
        // Zero members guard
        require(members.length > 0);

        // Only member with marker can to send transaction
        require(msg.sender == members[marker]);

        // Store transaction sending block
        keepaliveBlock = block.number;

        _;
    }

    function to(address _to, bytes _data) external keepalive quoted member
    { require(_to.call(_data)); }

    function () external keepalive quoted member
    { require(factory.call(msg.data)); }
}

pragma solidity ^0.4.24;

import './LighthouseAPI.sol';
import './LighthouseABI.sol';
import './LiabilityFactory.sol';

contract LighthouseLib is LighthouseAPI, LighthouseABI {

    function refill(uint256 _value) external {
        require(xrt.transferFrom(msg.sender, this, _value));
        require(_value >= minimalFreeze);

        if (balances[msg.sender] == 0) {
            indexOf[msg.sender] = members.length;
            members.push(msg.sender);
        }
        balances[msg.sender] += _value;
    }

    function withdraw(uint256 _value) external {
        require(balances[msg.sender] >= _value);

        require(xrt.transfer(msg.sender, _value));
        balances[msg.sender] -= _value;

        // Drop member if quota go to zero
        if (quotaOf(msg.sender) == 0) {
            require(xrt.transfer(msg.sender, balances[msg.sender]));
            balances[msg.sender] = 0;

            uint256 senderIndex = indexOf[msg.sender];
            uint256 lastIndex = members.length - 1;
            if (senderIndex < lastIndex)
                members[senderIndex] = members[lastIndex];
            members.length -= 1;
        }
    }

    function nextMember() internal {
        marker = (marker + 1) % members.length;
        quota = balances[members[marker]] / minimalFreeze;
    }

    modifier quoted {
        if (quota == 0) nextMember();
        quota -= 1;

        _;
    }

    modifier keepalive {
        if (timeoutBlocks < block.number - keepaliveBlock)
            while (msg.sender != members[marker])
                nextMember();

        _;
    }

    modifier member {
        require(members.length > 0);
        require(msg.sender == members[marker]);
        keepaliveBlock = block.number;

        _;
    }

    function to(address _to, bytes _data) external keepalive quoted member
    { require(_to.call(_data)); }

    function () external keepalive quoted member
    { require(factory.call(msg.data)); }
}


update import

update import

/*
    Copyright 2017 Phillip A. Elsasser

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

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

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

pragma solidity ^0.4.0;

import "Creatable.sol";
import "./Oraclize/oraclizeAPI.sol";

//TODO: fix style issues
//      add accounting for users and positions
//      how to hold ether for needed gas
//      how to hold collateral pool
//      adding failsafe for pool distribution.
contract MarketContract is Creatable, usingOraclize  {

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN;
    int public MAX_PRICE;
    int public MIN_PRICE;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT = 1 days;
    uint8 public BUY_SIDE = 0;
    uint8 public SELL_SIDE = 1;

    // state variables
    string public lastPriceQueryResult;
    int public lastPrice;
    bool public isExpired;
    mapping(bytes32 => bool) validQueryIDs;

    // how to map user to position?

    event NewOracleQuery(string description);
    event UpdatedLastPrice(string price);
    event ContractSettled();

    struct Order {
        address maker;
        address taker;
        uint qty;
        uint price;
        uint8 makerSide;
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    function MarketContract(
        string contractName,
        address baseToken,
        string oracleDataSource,
        string oracleQuery,
        int maxPrice,
        int minPrice,
        uint priceDecimalPlaces,
        uint daysToExpiration){

        require(maxPrice > minPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN = baseToken;
        MAX_PRICE = maxPrice;
        MIN_PRICE = minPrice;
        EXPIRATION = now + daysToExpiration * 1 days;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        queryOracle();  // schedules recursive calls to oracle
    }

    function __callback(bytes32 queryID, string result, bytes proof) {
        if (!validQueryIDs[queryID]) throw;
        if (msg.sender != oraclize_cbAddress()) throw;
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isExpired) {
            queryOracle();  // set up our next query
        }
    }

    function queryOracle() internal
    {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            NewOracleQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
        } else {
            NewOracleQuery("Oraclize query was sent, standing by for the answer..");
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() internal
    {
        if(isExpired)   // already expired.
            return;

        if(now > EXPIRATION) {
            isExpired = true;   // time based expiration has occurred.
        } else if(lastPrice >= MAX_PRICE || lastPrice <= MIN_PRICE) {
            isExpired = true;   // we have breached/touched our pricing bands
        }

        if(isExpired) {
            settleContract();
        }
    }

    function settleContract() internal
    {
        // TODO: build mechanism for distribution of collateral
        ContractSettled();
    }
}

/*
    Copyright 2017 Phillip A. Elsasser

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

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

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

pragma solidity ^0.4.0;

import "Creatable.sol";
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";

//TODO: fix style issues
//      add accounting for users and positions
//      how to hold ether for needed gas
//      how to hold collateral pool
//      adding failsafe for pool distribution.
contract MarketContract is Creatable, usingOraclize  {

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN;
    int public MAX_PRICE;
    int public MIN_PRICE;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT = 1 days;
    uint8 public BUY_SIDE = 0;
    uint8 public SELL_SIDE = 1;

    // state variables
    string public lastPriceQueryResult;
    int public lastPrice;
    bool public isExpired;
    mapping(bytes32 => bool) validQueryIDs;

    // how to map user to position?

    event NewOracleQuery(string description);
    event UpdatedLastPrice(string price);
    event ContractSettled();

    struct Order {
        address maker;
        address taker;
        uint qty;
        uint price;
        uint8 makerSide;
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    function MarketContract(
        string contractName,
        address baseToken,
        string oracleDataSource,
        string oracleQuery,
        int maxPrice,
        int minPrice,
        uint priceDecimalPlaces,
        uint daysToExpiration){

        require(maxPrice > minPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN = baseToken;
        MAX_PRICE = maxPrice;
        MIN_PRICE = minPrice;
        EXPIRATION = now + daysToExpiration * 1 days;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        queryOracle();  // schedules recursive calls to oracle
    }

    function __callback(bytes32 queryID, string result, bytes proof) {
        if (!validQueryIDs[queryID]) throw;
        if (msg.sender != oraclize_cbAddress()) throw;
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isExpired) {
            queryOracle();  // set up our next query
        }
    }

    function queryOracle() internal
    {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            NewOracleQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
        } else {
            NewOracleQuery("Oraclize query was sent, standing by for the answer..");
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() internal
    {
        if(isExpired)   // already expired.
            return;

        if(now > EXPIRATION) {
            isExpired = true;   // time based expiration has occurred.
        } else if(lastPrice >= MAX_PRICE || lastPrice <= MIN_PRICE) {
            isExpired = true;   // we have breached/touched our pricing bands
        }

        if(isExpired) {
            settleContract();
        }
    }

    function settleContract() internal
    {
        // TODO: build mechanism for distribution of collateral
        ContractSettled();
    }
}


Added descriptions for tags and corresponding functions

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Master()
  {
    tokenBalance[address(this)] = 1;
    address[] memory a;
    uint useless = addTag("account", a);
  }

  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0)
    {
      tokenBalance[user] = balance;
    }
  }
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name;
  }
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress;
  }
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment);
  }
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  function getTokenBalance(address user) returns(uint)
  {
    return tokenBalance[user];
  }
  function getTagName(address tagAddress) returns(string)
  {
    return tagName[tagAddress];
  }
  function getTagAddressFromName(string name) returns(address)
  {
    return tagAddressFromName[name];
  }
  function getAchievement(address user) returns(address[])
  {
    return achievements[user];
  }
  function getNumberOfachievments(address user) returns(uint)
  {
    return achievements[user].length;
  }
  function getAvailability(address user) returns(bool)
  {
    return availability[user] = true;
  }
  function getTagDescription(string nameOfTag) returns(string tagDescription)
  {
    return Tag(tagAddressFromName[nameOfTag]).getDescription();
  }

  function addUser()
  {
    User newUser = new User(msg.sender, address(this));
    Tag(tagAddressFromName["account"]).startAssessment(address(newUser),5);
  }

  function addTag(string name, address[] parentList, string description) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this), description);
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;
      tokenBalance[msg.sender] -= 1;
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  string description;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    uint _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress, string tagDescription)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
    description = tagDescription;
  }

  function getDescription() returns(string)
  {
    return description;
  }

  function getOwners() returns(address[])
  {
    return owners;
  }

  function getOwnerLength() returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) returns(address)
  {
      return owners[index];
  }

  function getParents() returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) returns(address)
  {
      return parentTags[index];
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessors(Assessment(assessment).getNumberOfAssessors());
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, uint score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).suicide();
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] potentialAssessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;
  uint referenceTime;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    referenceTime = block.timestamp;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessors(uint numberOfAssessorsNeeded)
  {
    for(uint i = 0; i < numberOfAssessorsNeeded; i++)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      assessors[randomAssessor] = 3;
      potentialAssessors.push(randomAssessor);
      User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
    }
  }

  function getAssessorPoolLength() returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
  function setTask(address assessorAddress, string data)
  {
    assessmentTasks[assessorAddress] = data;
  }

  function getTask(address assessorAddress) returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setAnswer(address assesseeAddress, string data)
  {
    assessmentAnswers[assesseeAddress] = data;
  }

  function getAnswer(address assesseeAddress) returns(string)
  {
    return assessmentAnswers[assesseeAddress];
  }

  function setResult(address assessorAddress, bool pass, uint score)
  {
    Results memory results;
    results.pass = pass;
    results.score = score;
    assessmentResults[assessorAddress] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function confirmAssessor(uint confirm)
  {
    if(assessors[msg.sender] != 0)
    {
      if(now - referenceTime <= 181)
      {
        assessors[msg.sender] = confirm;
        if(confirm == 1)
        {
          finalAssessors.push(msg.sender);
          User(msg.sender).notification("Confirmed As Assessing", tag, 2);
        }
        if(confirm == 2)
        {
          User(msg.sender).notification("Confirmed As Not Assessing", tag, 3);
        }
      }
      else
      {
        for(uint i = 0; i < potentialAssessors.length; i++)
        {
          if(assessors[msg.sender]==3)
          {
            User(msg.sender).notification("Did Not Respond In Time To Be Assessor", tag, 4);
            Master(master).mapTokenBalance(msg.sender, Master(master).getTokenBalance(msg.sender) - 1);
          }
        }
        if(numberOfAssessors - finalAssessors.length != 0)
        {
          uint remaining = numberOfAssessors - finalAssessors.length;
          delete potentialAssessors;
          referenceTime = now;
          setPotentialAssessors(remaining);
        }
      }
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  address master;
  address[] acheivements;
  string userData;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() returns(string)
  {
    return userData;
  }

  function suicide()
  {
    selfdestruct(master);
  }
}

contract Master
{
  //Initialize master data store
  mapping (address => uint) tokenBalance;
  mapping (address => string) tagName;
  mapping (string => address) tagAddressFromName;
  mapping (address => address[]) achievements;
  mapping (address => bool) availability;
  mapping (address => address) users;
  event TagCreation
  ( string _tagName,
    address _tagAddress,
    address[] _parents);

  function Master()
  {
    tokenBalance[address(this)] = 1;
    address[] memory a;
    uint useless = addTag("account", a);
  }

  function mapTokenBalance(address user, uint balance)
  {
    if(tagAddressFromName[tagName[msg.sender]] == 0)
    {
      tokenBalance[user] = balance;
    }
  }
  function mapTagName(address tagAddress, string name)
  {
    tagName[tagAddress] = name;
  }
  function mapTagAddressFromName(string name, address tagAddress)
  {
    tagAddressFromName[name] = tagAddress;
  }
  function mapAchievement(address user, address achievment)
  {
    achievements[user].push(achievment);
  }
  function mapAvailability(address user, bool available)
  {
    availability[user] = available;
  }

  function getTokenBalance(address user) returns(uint)
  {
    return tokenBalance[user];
  }
  function getTagName(address tagAddress) returns(string)
  {
    return tagName[tagAddress];
  }
  function getTagAddressFromName(string name) returns(address)
  {
    return tagAddressFromName[name];
  }
  function getAchievement(address user) returns(address[])
  {
    return achievements[user];
  }
  function getNumberOfachievments(address user) returns(uint)
  {
    return achievements[user].length;
  }
  function getAvailability(address user) returns(bool)
  {
    return availability[user] = true;
  }

  function addUser()
  {
    User newUser = new User(msg.sender, address(this));
    Tag(tagAddressFromName["account"]).startAssessment(address(newUser),5);
  }

  function addTag(string name, address[] parentList) returns(uint) //Creates a new tag contract
  {
    uint response = 0;
    address[] parents;
    if(tokenBalance[msg.sender] < 1)
    {
      response += 1;
    }
    if(tagAddressFromName[name] == 0)
    {
      response += 10;
    }
    if(response==0)
    {
      for(uint i=0; i<= parentList.length; i++) //adds all the given parents
      {
        if(parentList[i]==0)
        {
          response += 100*(10**i);
        }
        else
        {
          parents.push(parentList[i]);
        }
      }
      Tag newTag = new Tag(name, parents, address(this));
      address newTagAddress = address(newTag);
      tagName[newTagAddress] = name;
      tagAddressFromName[name] = newTagAddress;
      tokenBalance[msg.sender] -= 1;
      TagCreation(name, newTagAddress, parents);
    }
    return response;
  }
}

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parentTags;
  address master;
  string name;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  event CompletedAssessment
  ( address _assessee,
    bool _pass,
    uint _score,
    address _assessment);

  function Tag(string tagName, address[] parents, address masterAddress)
  {
    name = tagName;
    parentTags = parents;
    master = masterAddress;
  }

  function getOwners() returns(address[])
  {
    return owners;
  }

  function getOwnerLength() returns(uint)
  {
    return owners.length;
  }

  function getOwner(uint index) returns(address)
  {
      return owners[index];
  }

  function getParents() returns(address[])
  {
    return parentTags;
  }

  function getParentsLength() returns(uint)
  {
    return parentTags.length;
  }

  function getParent(uint index) returns(address)
  {
      return parentTags[index];
  }

  function setAssessorPool(address tagAddress, address assessment)
  {
    for(uint i = 0; i < Tag(tagAddress).getOwnerLength() && Assessment(assessment).getAssessmentPoolSize() != 0; i++)
    {
      if(Assessment(assessment).getAssessorPoolLength() < Tag(tagAddress).getOwnerLength()/10)
      {
        address random = Tag(tagAddress).getOwner(getRandom(Tag(tagAddress).getOwnerLength()-1));
        if(Master(master).getAvailability(random) == true)
        {
          Assessment(assessment).addToAssessorPool(random);
          Assessment(assessment).setAssessmentPoolSize(Assessment(assessment).getAssessmentPoolSize() -1);
        }
      }
      else
      {
        for(uint j = 0; j < Tag(tagAddress).getParentsLength(); j++)
        {
          setAssessorPool(Tag(tagAddress).getParent(j), assessment);
        }
      }
      if(Assessment(assessment).getAssessmentPoolSize() <= 0)
      {
        Assessment(assessment).setPotentialAssessors(Assessment(assessment).getNumberOfAssessors());
      }
    }
  }

  function startAssessment(address assessee, uint size)
  {
    Assessment newAssessment = new Assessment(assessee, address(this), master);
    newAssessment.setNumberOfAssessors(size);
    newAssessment.setAssessmentPoolSize(size*20);
    setAssessorPool(address(this), address(newAssessment));
  }

  function finishAssessment(bool pass, uint score, address assessee, address assessment)
  {
    if(pass == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
      Master(master).mapAchievement(assessee,address(this));
    }
    if(pass == false && address(this) == Master(master).getTagAddressFromName("account"))
    {
      User(assessee).suicide();
    }
    assessmentHistory[assessee].push(assessment);
    CompletedAssessment(assessee, pass, score, assessment);
  }
  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessorPool;
  mapping (address => uint) assessors;
  address[] potentialAssessors;
  address[] finalAssessors;
  address tag;
  address master;
  uint poolSizeRemaining;
  uint numberOfAssessors;
  struct Results
  {
    bool pass;
    uint score;
  }
  mapping(address => string) assessmentTasks; //Given by the assessors as IPFS hashes
  mapping(address => string) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => Results) assessmentResults; //Pass/Fail and Score given by assessors
  uint finalScore;
  bool finalResult;
  uint referenceTime;

  function Assessment(address assesseeAddress, address tagAddress, address masterAddress)
  {
    assessee = assesseeAddress;
    tag = tagAddress;
    master = masterAddress;
    referenceTime = block.timestamp;
  }

  function setNumberOfAssessors(uint number)
  {
    numberOfAssessors = number;
  }

  function getNumberOfAssessors() returns(uint)
  {
    return numberOfAssessors;
  }

  function setAssessmentPoolSize(uint sizeRemaining)
  {
    poolSizeRemaining = sizeRemaining;
  }

  function getAssessmentPoolSize() returns(uint)
  {
    return poolSizeRemaining;
  }

  function addToAssessorPool(address potentialAddress)
  {
    assessorPool.push(potentialAddress);
  }

  function setPotentialAssessors(uint numberOfAssessorsNeeded)
  {
    for(uint i = 0; i < numberOfAssessorsNeeded; i++)
    {
      address randomAssessor = assessorPool[getRandom(assessorPool.length)];
      assessors[randomAssessor] = 3;
      potentialAssessors.push(randomAssessor);
      User(randomAssessor).notification("Called As A Potential Assessor",tag, 1);
    }
  }

  function getAssessorPoolLength() returns(uint)
  {
      return assessorPool.length;
  }

  function getRandom(uint i) returns(uint)
  {
    return 12;
  }
  function setTask(address assessorAddress, string data)
  {
    assessmentTasks[assessorAddress] = data;
  }

  function getTask(address assessorAddress) returns(string)
  {
    return assessmentTasks[assessorAddress];
  }

  function setAnswer(address assesseeAddress, string data)
  {
    assessmentAnswers[assesseeAddress] = data;
  }

  function getAnswer(address assesseeAddress) returns(string)
  {
    return assessmentAnswers[assesseeAddress];
  }

  function setResult(address assessorAddress, bool pass, uint score)
  {
    Results memory results;
    results.pass = pass;
    results.score = score;
    assessmentResults[assessorAddress] = results;
  }

  function assess()
  {

  }

  function calculateResult()
  {

  }

  function confirmAssessor(uint confirm)
  {
    if(assessors[msg.sender] != 0)
    {
      if(now - referenceTime <= 181)
      {
        assessors[msg.sender] = confirm;
        if(confirm == 1)
        {
          finalAssessors.push(msg.sender);
          User(msg.sender).notification("Confirmed As Assessing", tag, 2);
        }
        if(confirm == 2)
        {
          User(msg.sender).notification("Confirmed As Not Assessing", tag, 3);
        }
      }
      else
      {
        for(uint i = 0; i < potentialAssessors.length; i++)
        {
          if(assessors[msg.sender]==3)
          {
            User(msg.sender).notification("Did Not Respond In Time To Be Assessor", tag, 4);
            Master(master).mapTokenBalance(msg.sender, Master(master).getTokenBalance(msg.sender) - 1);
          }
        }
        if(numberOfAssessors - finalAssessors.length != 0)
        {
          uint remaining = numberOfAssessors - finalAssessors.length;
          delete potentialAssessors;
          referenceTime = now;
          setPotentialAssessors(remaining);
        }
      }
    }
  }

  function returnResults()
  {
    Tag(tag).finishAssessment(finalResult, finalScore, assessee, address(this));
  }

  function cashout()
  {

  }
}

contract User
{
  address user;
  address master;
  address[] acheivements;
  string userData;

  event Notification
  ( string _description,
    address _sender,
    address _user,
    address _tag,
    uint _code);

  function User(address userAddress, address masterAddress)
  {
    user = userAddress;
    master = masterAddress;
  }

  function notification(string description, address tag, uint code)
  {
    Notification(description, msg.sender, address(this), tag, code);
  }

  function setUserData(string hash)
  {
    userData = hash;
  }

  function getUserData() returns(string)
  {
    return userData;
  }

  function suicide()
  {
    selfdestruct(master);
  }
}


started use case updates...

started use case updates...

pragma solidity ^0.4.2;
/*
We are starting from a simple identity contract supporting KYC using hashes computed off chain, e.g.

sha-1(NAME/GIVENNAMES/DATEOFBIRTH/PLACEOFBIRTH/NATIONALIY/EMAIL/ADDRESS1/ADDRESS2/ADDRESS3/RANDOM)

Specific factors to be hashed in identity hash for RChain is TBD. No identity factors are exposed on the blockchain.

For rchain membership we will need claimed and verified mappings and may want to use encrypted rather than hashed credentials.

Use case using hashes:
1. a membership request is made off chain by supplying rhain with their identity factors kept off chain. (google form)
2. The hashed identity with a random string at the end is emailed to the user verifying email address.
3. The user requests membership paying cost fopr verification (member fee) to become a claimed identity giving the hash of their identity factors they received by email from an ethereum address they own..
4. Requests are handled manually.
5. a code (based on member hash) is sent to the user by postal mail offline.
6. another is sent by email, another by sms
7. When the user receives the key by mail they invoke a validator method in the contract which attempts to decode the verification string and conditionally adds the ethereum address as a corroboration of the mailing address and email to the verified mapping.
8. The contract owner (rchain) can revoke a corroboration. Later, in the case of rejection of a membership request, the eth should be returned returned if it can be done safely. Returning the eth offline is safer.
*/
// work in progress, starting from a simple solidity example, comment and suggest edits.
// TODO make Owned?
contract MemberIdentityRegistry {
    // TODO bring up to standards (amp/rhoc/divvy bounties, work claimed in comments)
    //   http://solidity.readthedocs.io/en/develop/layout-of-source-files.html
    //   http://solidity.readthedocs.io/en/develop/style-guide.html
    //   http://solidity.readthedocs.io/en/develop/structure-of-a-contract.html
    //   http://solidity.readthedocs.io/en/develop/common-patterns.html
    //   http://solidity.readthedocs.io/en/develop/security-considerations.html
    // TODO test and debug
    address public owner = msg.sender;
    address public wallet; // wallet to receive membership fees
    uint256 public price = 1 ether;
    mapping(address => mapping(bytes32 => uint256)) public verified;
    mapping(address => mapping(bytes32 => uint256)) claimed;
    mapping(address => mapping(bytes32 => uint256)) code;
    mapping(address => address) uPortAddress; // future use

    event Registered(uint256 hash);
    event Verified(uint256 hash);

    function setPrice(uint256 value) {
        if(owner != msg.sender) throw;
        price = value;
    }

    function setWallet(address addr) {
        if(owner != msg.sender) throw;
        wallet = addr;
    }
        function claimIFactor(bytes32 factor, uint256 hash) payable {
        // receive eth coop membership fee
	    // if(msg.value != 1 ether) throw;
	    if ( ! wallet.send(msg.value) ) throw;
        claimed[msg.sender][factor] = hash;
        Registered(hash);
    }

    function verifyCode(bytes32 factor, uint256 key) returns (bool) {
        uint256 encrypted = code[msg.sender][factor];
        // TODO decode encrypted with key better than product of primes
        bool decoded = encrypted == (key % 1000000) * (key/1000000);
        if ( decoded ) {
            uint256 hash = claimed[msg.sender][factor];
            verified[msg.sender][factor] = hash;
            verified[msg.sender]["email"] = hash;
            Verified(hash);
            return true;
        }
        return false;
    }

    function setCode(address who, bytes32 factor, uint256 encrypted) {
        if(owner != msg.sender) throw;
        code[who][factor] = encrypted;
    }

    function verifyIdentity(address who, bytes32 factor, uint256 hash) constant returns(bool) {
        return verified[who][factor] == hash;
    }
    function bringuPort(address uPortId) {   // for future
        // TODO verify address belongs to uPortId
   // corroborate address in uPort ID
        uPortAddress[msg.sender] = uPortId;
    }

    function getHashID(address who, bytes32 factor) returns (uint256) {
        return claimed[who][factor];
    }
    function getVerifiedHashID(address who, bytes32 factor) returns (uint256) {
        return verified[who][factor];
    }

    function revoke(bytes32 factor,address who) {
        if(owner != msg.sender) throw;
        delete verified[who][factor];
    }
    function revokeMyId(bytes32 factor) {
       delete verified[msg.sender][factor];
    }
    // function revokeAll(bytes32 factor, address who) {
    //     if(owner != msg.sender) throw;
    //     verified[who] = new mapping(bytes32 => uint256);
    // }
    // function revokeAllMyId() {
    //   delete verified[msg.sender];
    // }
}

pragma solidity ^0.4.2;
/*
We are starting from a simple identity contract supporting KYC using hashes computed off chain, e.g.

sha-1(NAME/GIVENNAMES/DATEOFBIRTH/PLACEOFBIRTH/NATIONALIY/EMAIL/ADDRESS1/ADDRESS2/ADDRESS3/RANDOM)

Specific factors to be hashed in identity hash for RChain is TBD. No identity factors are exposed on the blockchain.

For rchain membership we will need claimed and verified mappings and may want to use encrypted rather than hashed credentials.

Use case using hashes:
1. a membership request is made off chain by supplying rhain with their identity factors kept off chain. (google form?)
2. The hashed identity with a random string at the end is emailed to the user verifying email address.
3. The user requests membership paying 1 eth to become a claimed identity giving the hash of their identity factors they received by email from an ethereum address they own..
4. Requests are handled manually and then registered on the blockchain
5. a random key is sent to the user by postal mail offline.
6. An encrypted value by that key is sent to the contract and set in the code mapping.
7. When the user receives the key by mail they invoke a validator method in the contract which attempts to decode the verification string and conditionally adds the ethereum address as a corroboration of the mailing address and email to the verified mapping.
8. The contract owner (rchain) can revoke a corroboration. Later, in the case of rejection of a membership request, the eth should be returned returned if it can be done safely. Returning the eth offline is safer.
*/
// work in progress, starting from a simple solidity example, comment and suggest edits.
// TODO make Owned?
contract MemberIdentityRegistry {
    // TODO bring up to standards (amp/rhoc/divvy bounties, work claimed in comments)
    //   http://solidity.readthedocs.io/en/develop/layout-of-source-files.html
    //   http://solidity.readthedocs.io/en/develop/style-guide.html
    //   http://solidity.readthedocs.io/en/develop/structure-of-a-contract.html
    //   http://solidity.readthedocs.io/en/develop/common-patterns.html
    //   http://solidity.readthedocs.io/en/develop/security-considerations.html
    // TODO test and debug
    address public owner = msg.sender;
    address public wallet; // wallet to receive membership fees
    uint256 public price = 1 ether;
    mapping(address => mapping(bytes32 => uint256)) public verified;
    mapping(address => mapping(bytes32 => uint256)) claimed;
    mapping(address => mapping(bytes32 => uint256)) code;
    mapping(address => address) uPortAddress; // future use

    event Registered(uint256 hash);
    event Verified(uint256 hash);

    function setPrice(uint256 value) {
        if(owner != msg.sender) throw;
        price = value;
    }

    function setWallet(address addr) {
        if(owner != msg.sender) throw;
        wallet = addr;
    }
        function claimIFactor(bytes32 factor, uint256 hash) payable {
        // receive eth coop membership fee
	    // if(msg.value != 1 ether) throw;
	    if ( ! wallet.send(msg.value) ) throw;
        claimed[msg.sender][factor] = hash;
        Registered(hash);
    }

    function verifyCode(bytes32 factor, uint256 key) returns (bool) {
        uint256 encrypted = code[msg.sender][factor];
        // TODO decode encrypted with key better than product of primes
        bool decoded = encrypted == (key % 1000000) * (key/1000000);
        if ( decoded ) {
            uint256 hash = claimed[msg.sender][factor];
            verified[msg.sender][factor] = hash;
            verified[msg.sender]["email"] = hash;
            Verified(hash);
            return true;
        }
        return false;
    }

    function setCode(address who, bytes32 factor, uint256 encrypted) {
        if(owner != msg.sender) throw;
        code[who][factor] = encrypted;
    }

    function verifyIdentity(address who, bytes32 factor, uint256 hash) constant returns(bool) {
        return verified[who][factor] == hash;
    }
    function bringuPort(address uPortId) {   // for future
        // TODO verify address belongs to uPortId
   // corroborate address in uPort ID
        uPortAddress[msg.sender] = uPortId;
    }

    function getHashID(address who, bytes32 factor) returns (uint256) {
        return claimed[who][factor];
    }
    function getVerifiedHashID(address who, bytes32 factor) returns (uint256) {
        return verified[who][factor];
    }

    function revoke(bytes32 factor,address who) {
        if(owner != msg.sender) throw;
        delete verified[who][factor];
    }
    function revokeMyId(bytes32 factor) {
       delete verified[msg.sender][factor];
    }
    // function revokeAll(bytes32 factor, address who) {
    //     if(owner != msg.sender) throw;
    //     verified[who] = new mapping(bytes32 => uint256);
    // }
    // function revokeAllMyId() {
    //   delete verified[msg.sender];
    // }
}


Cleanup and fixes for exchange matching.

contract Custodian {
    function reserve(address owner, uint amount);
    function unreserve(address owner, uint amount);
    function transfer(address oldOwner, address newOwner, uint shares);
}

contract Exchange {
    enum Side { Bid, Ask }
    struct Offer {
        address owner;
        uint price;
        bool active;
        Side side;
        int16 nextIndex;
        uint32 shares;
    }

    int32 constant maximumDepth = 1000;
    int16 topAskIndex = -1;
    int16 topBidIndex = -1;
    mapping (address => uint) balances;
    Custodian custodian;
    Offer[1000] offers;

    function Exchange() {
        custodian = Custodian(msg.sender);
    }

    function postOffer(Side side, uint price, uint32 shares) {
        if (price == 0 || shares == 0) {
            throw;
        }
        if (side == Side.Bid) {
            if (price * shares != msg.value) {
                throw;
            }
        } else {
            custodian.reserve(msg.sender, shares);
        }
        uint32 remainingShares = shares;
        int16 index = side == Side.Ask ? topBidIndex : topAskIndex;
        int16 parentIndex = -1;
        while (index != -1 && remainingShares > 0) {
            Offer next = offers[uint(index)];
            bool matches = (
                (side == Side.Bid && next.price <= price) ||
                (side == Side.Ask && next.price >= price)
            );
            if (!next.active || !matches) {
                break;
            }
            address seller = side == Side.Bid ? next.owner : msg.sender;
            address buyer = side == Side.Bid ? msg.sender : next.owner;
            if (next.shares <= remainingShares) {
                balances[seller] += next.shares * next.price;
                remainingShares -= next.shares;
                offers[uint(index)].active = false;
                spliceOffer(next.side, parentIndex, next.nextIndex);
                custodian.unreserve(seller, next.shares);
                custodian.transfer(seller, buyer, next.shares);
            } else {
                balances[seller] += remainingShares * next.price;
                offers[uint(index)].shares = next.shares - remainingShares;
                custodian.unreserve(seller, remainingShares);
                custodian.transfer(seller, buyer, remainingShares);
                remainingShares = 0;
            }
            parentIndex = index;
            index = next.nextIndex;
        }
        if (remainingShares == 0) {
            return;
        }
        int16 newIndex = 0;
        while (offers[uint(newIndex)].active) {
            if (++newIndex >= maximumDepth) {
                throw;
            }
        }
        int16 previousIndex = -1;
        int16 nextIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (nextIndex != -1 && offers[uint(nextIndex)].price > price) {
            previousIndex = nextIndex;
            nextIndex = offers[uint(nextIndex)].nextIndex;
        }
        if (previousIndex == -1) {
            if (side == Side.Bid) {
                topBidIndex = newIndex;
            } else {
                topAskIndex = newIndex;
            }
        } else {
            offers[uint(previousIndex)].nextIndex = newIndex;
        }
        offers[uint(newIndex)] = Offer(msg.sender, price, true, side, nextIndex, remainingShares);
    }

    function getNumberOfOffers(Side side) returns (uint16) {
        uint16 count = 0;
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (currentIndex != -1) {
            count++;
            currentIndex = offers[uint(currentIndex)].nextIndex;
        }
        return count;
    }

    function getTopOffer(Side side) returns (uint price, uint32 shares) {
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        if (currentIndex == -1) {
            return (0, 0);
        }
        price = offers[uint(currentIndex)].price;
        shares = offers[uint(currentIndex)].shares;
    }

    function cancelOffer(Side side, uint price, uint32 shares) {
        bool found = false;
        int16 parentIndex = -1;
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (currentIndex != -1 && !found) {
            Offer offer = offers[uint(currentIndex)];
            found = (
                offer.owner == msg.sender &&
                offer.side == side &&
                offer.price == price &&
                offer.shares >= shares
            );
            if (found) {
                break;
            }
            parentIndex = currentIndex;
            currentIndex = offer.nextIndex;
        }
        if (!found) {
            throw;
        }
        Offer matchingOffer = offers[uint(currentIndex)];
        uint32 remainingShares = shares - matchingOffer.shares;
        if (remainingShares > 0) {
            offers[uint(currentIndex)].shares -= shares;
        } else {
            matchingOffer.active = false;
            spliceOffer(side, parentIndex, matchingOffer.nextIndex);
        }
        if (side == Side.Bid) {
            balances[msg.sender] += price * shares;
        } else {
            custodian.unreserve(msg.sender, shares);
        }
    }

    function withdrawal() {
        uint amount = balances[msg.sender];
        if (amount == 0) {
            throw;
        }
        balances[msg.sender] = 0;
        if (!msg.sender.send(amount)) {
            throw;
        }
    }

    function getBalance() returns (uint) {
        return balances[msg.sender];
    }

    function spliceOffer(Side side, int16 parentIndex, int16 nextIndex) private {
        if (parentIndex != -1) {
            offers[uint(parentIndex)].nextIndex = nextIndex;
        } else {
            if (side == Side.Bid) {
                topBidIndex = nextIndex;
            } else {
                topAskIndex = nextIndex;
            }
        }
    }

    function() {
        throw;
    }
}

contract Custodian {
    function reserve(address owner, uint amount);
    function unreserve(address owner, uint amount);
    function transfer(address oldOwner, address newOwner, uint shares);
}

contract Exchange {
    enum Side { Bid, Ask }
    struct Offer {
        address owner;
        uint price;
        bool active;
        Side side;
        int16 nextIndex;
        uint32 shares;
    }

    int32 constant maximumDepth = 1000;
    int16 topAskIndex = -1;
    int16 topBidIndex = -1;
    mapping (address => uint) balances;
    Custodian custodian;
    Offer[1000] offers;

    function Exchange() {
        custodian = Custodian(msg.sender);
    }

    function postOffer(Side side, uint price, uint32 shares) {
        if (price == 0 || shares == 0) {
            throw;
        }
        if (side == Side.Bid) {
            if (price * shares != msg.value) {
                throw;
            }
        } else {
            custodian.reserve(msg.sender, shares);
        }
        uint32 remainingShares = shares;
        int16 top = side == Side.Ask ? topBidIndex : topAskIndex;
        if (top != -1) {
            int16 index = top;
            Offer next = offers[uint(top)];
            bool matches = (
                (side == Side.Bid && next.price <= price) ||
                (side == Side.Ask && next.price >= price)
            );
            while (next.active && matches) {
                address seller = side == Side.Bid ? next.owner : msg.sender;
                address buyer = side == Side.Bid ? msg.sender : next.owner;
                if (next.shares <= remainingShares) {
                    balances[seller] = next.shares * next.price;
                    remainingShares -= next.shares;
                    offers[uint(index)].active = false;
                    custodian.unreserve(seller, next.shares);
                    custodian.transfer(seller, buyer, next.shares);
                } else {
                    balances[seller] = remainingShares * next.price;
                    offers[uint(index)].shares = next.shares - remainingShares;
                    remainingShares = 0;
                    custodian.unreserve(seller, remainingShares);
                    custodian.transfer(seller, buyer, remainingShares);
                }
                index = next.nextIndex;
                next = offers[uint(index)];
                matches = (
                    (side == Side.Bid && next.price <= price) ||
                    (side == Side.Ask && next.price >= price)
                );
            }
        }
        if (remainingShares < 0) {
            throw;
        }
        if (remainingShares == 0) {
            return;
        }
        int16 newIndex = 0;
        while (offers[uint(newIndex)].active) {
            if (++newIndex >= maximumDepth) {
                throw;
            }
        }
        int16 previousIndex = -1;
        int16 nextIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (nextIndex != -1 && offers[uint(nextIndex)].price > price) {
            previousIndex = nextIndex;
            nextIndex = offers[uint(nextIndex)].nextIndex;
        }
        if (previousIndex == -1) {
            if (side == Side.Bid) {
                topBidIndex = newIndex;
            } else {
                topAskIndex = newIndex;
            }
        } else {
            offers[uint(previousIndex)].nextIndex = newIndex;
        }
        offers[uint(newIndex)] = Offer(msg.sender, price, true, side, nextIndex, remainingShares);
    }

    function getNumberOfOffers(Side side) returns (uint16) {
        uint16 count = 0;
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (currentIndex != -1) {
            count++;
            currentIndex = offers[uint(currentIndex)].nextIndex;
        }
        return count;
    }

    function getTopOffer(Side side) returns (uint price, uint32 shares) {
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        if (currentIndex == -1) {
            return (0, 0);
        }
        price = offers[uint(currentIndex)].price;
        shares = offers[uint(currentIndex)].shares;
    }

    function cancelOffer(Side side, uint price, uint32 shares) {
        bool found = false;
        int16 parentIndex = -1;
        int16 currentIndex = side == Side.Bid ? topBidIndex : topAskIndex;
        while (currentIndex != -1 && !found) {
            Offer offer = offers[uint(currentIndex)];
            found = (
                offer.owner == msg.sender &&
                offer.side == side &&
                offer.price == price &&
                offer.shares >= shares
            );
            if (found) {
                break;
            }
            parentIndex = currentIndex;
            currentIndex = offer.nextIndex;
        }
        if (!found) {
            throw;
        }
        Offer matchingOffer = offers[uint(currentIndex)];
        uint32 remainingShares = shares - matchingOffer.shares;
        if (remainingShares > 0) {
            offers[uint(currentIndex)].shares -= shares;
        } else {
            matchingOffer.active = false;
            if (parentIndex != -1) {
                offers[uint(parentIndex)].nextIndex = matchingOffer.nextIndex;
            } else {
                if (side == Side.Bid) {
                    topBidIndex = matchingOffer.nextIndex;
                } else {
                    topAskIndex = matchingOffer.nextIndex;
                }
            }
        }
        if (side == Side.Bid) {
            balances[msg.sender] += price * shares;
        } else {
            custodian.unreserve(msg.sender, shares);
        }
    }

    function withdrawal() {
        uint amount = balances[msg.sender];
        if (amount == 0) {
            throw;
        }
        balances[msg.sender] = 0;
        if (!msg.sender.send(amount)) {
            throw;
        }
    }

    function getBalance() returns (uint) {
        return balances[msg.sender];
    }

    function() {
        throw;
    }
}


comments

comments

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

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

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

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

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

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

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

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

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

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

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

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

        pot.isOpen = false;
        LogPotClosed(name);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot
            LogPotExpired(name);

            // return money minus 1% fee
            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100);
                totalRefunds += ((pot.buyIn * 99) / 100);
            }

            return;
        }

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

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

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

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

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

        msg.sender.transfer(refund); // this'll throw and restore state on failure
    }
}

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

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

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

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

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

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

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

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

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

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

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

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

        pot.isOpen = false;
        LogPotClosed(name);

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot
            LogPotExpired(name);

            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100); // return money minus 1% fee
                totalRefunds += ((pot.buyIn * 99) / 100); // return money minus 1% fee
            }
            return;
        }

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

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

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

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

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

        msg.sender.transfer(refund); // this'll throw and restore state on failure
    }
}


indexed params in events

indexed params in events

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

    event newPot(string indexed name, uint buyIn, address indexed firstPlayer);
    event potJoin(string indexed name, address indexed newPlayer);
    event potFull(string indexed name);
    event potExpired(string indexed name);
    event potClosed(string indexed name, address indexed winner1, address indexed winner2, address indexed loser);

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

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

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

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

        newPot(name, msg.value, msg.sender);
        potJoin(name, msg.sender);
    }

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

        pot.players.push(msg.sender);
        potJoin(name, msg.sender);

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

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

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

        pot.isOpen = false;

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot

            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100); // return money minus 1% fee
                totalRefunds += ((pot.buyIn * 99) / 100); // return money minus 1% fee
            }

            potExpired(name);
            return;
        }

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

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

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

        potClosed(name, winner1, winner2, loser);
    }

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

        msg.sender.transfer(refund); // this'll throw and restore state on failure
    }
}

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

    event newPot(string name, uint buyIn, address creatorPlayer);
    event potJoin(string name, address newPlayer);
    event potFull(string name);
    event potExpired(string name);
    event potClosed(string name, address winner1, address winner2, address loser);

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

    function getTotal() constant returns (uint){
        require(msg.sender == owner);

        return this.balance - totalRefunds;
    }

    function ownerWithdraw(){
        require(msg.sender == owner);

        int toWithdraw = int(this.balance) - int(100 finney) - int(totalRefunds); // leave 0.1 ether for gas (?)
        require(toWithdraw > 0);

        owner.transfer(uint(toWithdraw));
    }

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

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

        newPot(name, msg.value, msg.sender);
    }

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

        pot.players.push(msg.sender);
        potJoin(name, msg.sender);

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

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

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

        pot.isOpen = false;

        bytes32 blockHash = block.blockhash(pot.lastPlayerBlockNumber + 1);
        if(blockHash == 0) { // pot expired due to hash storage limits - players didn't solve pot

            for(uint i = 0; i < pot.players.length; i++){
                refunds[pot.players[i]] += ((pot.buyIn * 99) / 100); // return money minus 1% fee
                totalRefunds += ((pot.buyIn * 99) / 100); // return money minus 1% fee
            }

            potExpired(name);
            return;
        }

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

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

        uint winAmount = ((pot.buyIn / 2) * 99) / 100; // split the buy-in and take 1% fee
        uint refundAmount = pot.buyIn + winAmount;

        refunds[winner1] += refundAmount;
        refunds[winner2] += refundAmount;
        totalRefunds += refundAmount * 2;

        potClosed(name, winner1, winner2, loser);
    }

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

        msg.sender.transfer(refund); // this'll throw and restore state on failure
    }
}


Improve IRelayRecipient no-impl note

Improve IRelayRecipient no-impl note

pragma solidity ^0.5.0;

/**
 * @dev Base interface for a contract that will be called via the GSN from {IRelayHub}.
 *
 * TIP: You don't need to write an implementation yourself! Inherit from {GSNRecipient} instead.
 */
contract IRelayRecipient {
    /**
     * @dev Returns the address of the {IRelayHub} instance this recipient interacts with.
     */
    function getHubAddr() public view returns (address);

    /**
     * @dev Called by {IRelayHub} to validate if this recipient accepts being charged for a relayed call. Note that the
     * recipient will be charged regardless of the execution result of the relayed call (i.e. if it reverts or not).
     *
     * The relay request was originated by `from` and will be served by `relay`. `encodedFunction` is the relayed call
     * calldata, so its first four bytes are the function selector. The relayed call will be forwarded `gasLimit` gas,
     * and the transaction executed with a gas price of at least `gasPrice`. `relay`'s fee is `transactionFee`, and the
     * recipient will be charged at most `maxPossibleCharge` (in wei). `nonce` is the sender's (`from`) nonce for
     * replay attack protection in {IRelayHub}, and `approvalData` is a optional parameter that can be used to hold a signature
     * over all or some of the previous values.
     *
     * Returns a tuple, where the first value is used to indicate approval (0) or rejection (custom non-zero error code,
     * values 1 to 10 are reserved) and the second one is data to be passed to the other {IRelayRecipient} functions.
     *
     * {acceptRelayedCall} is called with 50k gas: if it runs out during execution, the request will be considered
     * rejected. A regular revert will also trigger a rejection.
     */
    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    )
        external
        view
        returns (uint256, bytes memory);

    /**
     * @dev Called by {IRelayHub} on approved relay call requests, before the relayed call is executed. This allows to e.g.
     * pre-charge the sender of the transaction.
     *
     * `context` is the second value returned in the tuple by {acceptRelayedCall}.
     *
     * Returns a value to be passed to {postRelayedCall}.
     *
     * {preRelayedCall} is called with 100k gas: if it runs out during exection or otherwise reverts, the relayed call
     * will not be executed, but the recipient will still be charged for the transaction's cost.
     */
    function preRelayedCall(bytes calldata context) external returns (bytes32);

    /**
     * @dev Called by {IRelayHub} on approved relay call requests, after the relayed call is executed. This allows to e.g.
     * charge the user for the relayed call costs, return any overcharges from {preRelayedCall}, or perform
     * contract-specific bookkeeping.
     *
     * `context` is the second value returned in the tuple by {acceptRelayedCall}. `success` is the execution status of
     * the relayed call. `actualCharge` is an estimate of how much the recipient will be charged for the transaction,
     * not including any gas used by {postRelayedCall} itself. `preRetVal` is {preRelayedCall}'s return value.
     *
     *
     * {postRelayedCall} is called with 100k gas: if it runs out during execution or otherwise reverts, the relayed call
     * and the call to {preRelayedCall} will be reverted retroactively, but the recipient will still be charged for the
     * transaction's cost.
     */
    function postRelayedCall(bytes calldata context, bool success, uint256 actualCharge, bytes32 preRetVal) external;
}

pragma solidity ^0.5.0;

/**
 * @dev Base interface for a contract that will be called via the GSN from {IRelayHub}. Do not use directly, inherit from
 * {GSNRecipient} instead.
 */
contract IRelayRecipient {
    /**
     * @dev Returns the address of the {IRelayHub} instance this recipient interacts with.
     */
    function getHubAddr() public view returns (address);

    /**
     * @dev Called by {IRelayHub} to validate if this recipient accepts being charged for a relayed call. Note that the
     * recipient will be charged regardless of the execution result of the relayed call (i.e. if it reverts or not).
     *
     * The relay request was originated by `from` and will be served by `relay`. `encodedFunction` is the relayed call
     * calldata, so its first four bytes are the function selector. The relayed call will be forwarded `gasLimit` gas,
     * and the transaction executed with a gas price of at least `gasPrice`. `relay`'s fee is `transactionFee`, and the
     * recipient will be charged at most `maxPossibleCharge` (in wei). `nonce` is the sender's (`from`) nonce for
     * replay attack protection in {IRelayHub}, and `approvalData` is a optional parameter that can be used to hold a signature
     * over all or some of the previous values.
     *
     * Returns a tuple, where the first value is used to indicate approval (0) or rejection (custom non-zero error code,
     * values 1 to 10 are reserved) and the second one is data to be passed to the other {IRelayRecipient} functions.
     *
     * {acceptRelayedCall} is called with 50k gas: if it runs out during execution, the request will be considered
     * rejected. A regular revert will also trigger a rejection.
     */
    function acceptRelayedCall(
        address relay,
        address from,
        bytes calldata encodedFunction,
        uint256 transactionFee,
        uint256 gasPrice,
        uint256 gasLimit,
        uint256 nonce,
        bytes calldata approvalData,
        uint256 maxPossibleCharge
    )
        external
        view
        returns (uint256, bytes memory);

    /**
     * @dev Called by {IRelayHub} on approved relay call requests, before the relayed call is executed. This allows to e.g.
     * pre-charge the sender of the transaction.
     *
     * `context` is the second value returned in the tuple by {acceptRelayedCall}.
     *
     * Returns a value to be passed to {postRelayedCall}.
     *
     * {preRelayedCall} is called with 100k gas: if it runs out during exection or otherwise reverts, the relayed call
     * will not be executed, but the recipient will still be charged for the transaction's cost.
     */
    function preRelayedCall(bytes calldata context) external returns (bytes32);

    /**
     * @dev Called by {IRelayHub} on approved relay call requests, after the relayed call is executed. This allows to e.g.
     * charge the user for the relayed call costs, return any overcharges from {preRelayedCall}, or perform
     * contract-specific bookkeeping.
     *
     * `context` is the second value returned in the tuple by {acceptRelayedCall}. `success` is the execution status of
     * the relayed call. `actualCharge` is an estimate of how much the recipient will be charged for the transaction,
     * not including any gas used by {postRelayedCall} itself. `preRetVal` is {preRelayedCall}'s return value.
     *
     *
     * {postRelayedCall} is called with 100k gas: if it runs out during execution or otherwise reverts, the relayed call
     * and the call to {preRelayedCall} will be reverted retroactively, but the recipient will still be charged for the
     * transaction's cost.
     */
    function postRelayedCall(bytes calldata context, bool success, uint256 actualCharge, bytes32 preRetVal) external;
}