Refactor updateStatus for legibility

Refactor updateStatus for legibility

pragma solidity ^0.4.11;

import "./historical/StandardToken.sol";
import "./PLCRVoting.sol";
import "./Parameterizer.sol";

contract Registry {

    // ------
    // EVENTS
    // ------

    event _Application(string domain, uint deposit);
    event _Challenge(string domain, uint deposit, uint pollID);
    event _Deposit(string domain, uint added, uint newTotal);
    event _Withdrawal(string domain, uint withdrew, uint newTotal);
    event _NewDomainWhitelisted(string domain);
    event _ApplicationRemoved(string domain);
    event _ListingRemoved(string domain);
    event _ChallengeFailed(uint challengeID);
    event _ChallengeSucceeded(uint challengeID);
    event _RewardClaimed(address voter, uint challengeID, uint reward);

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

    struct Challenge {
        uint rewardPool;        // (remaining) pool of tokens distributed amongst winning voters
        address challenger;     // owner of Challenge
        bool resolved;          // indication of if challenge is resolved
        uint stake;             // number of tokens at risk for either party during challenge
        uint totalTokens;       // (remaining) amount of tokens used for voting by the winning side
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) public challengeMap;
    // maps domainHashes to associated listing data
    mapping(bytes32 => Listing) public listingMap;
    // maps challengeIDs and address to token claim data
    mapping(uint => mapping(address => bool)) public tokenClaims;

    // Global Variables
    StandardToken public token;
    PLCRVoting public voting;
    Parameterizer public parameterizer;

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

    function Registry(
        address _tokenAddr,
        address _paramsAddr
    ) {
        token = StandardToken(_tokenAddr);
        parameterizer = Parameterizer(_paramsAddr);
        voting = new PLCRVoting(_tokenAddr);
    }

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

    //Allow a user to start an application
    //take tokens from user and set apply stage end time
    function apply(string domain, uint amount) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));
        require(amount >= parameterizer.get("minDeposit"));

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

        //transfer tokens
        require(token.transferFrom(listing.owner, this, amount));

        //set apply stage end time
        listing.applicationExpiry = block.timestamp + parameterizer.get("applyStageLen");
        listing.currentDeposit = amount;

        _Application(domain, amount);
    }

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

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

        listing.currentDeposit += amount;

        _Deposit(domain, amount, listing.currentDeposit);
    }

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

        require(listing.owner == msg.sender);
        require(amount <= listing.currentDeposit);
        require(listing.currentDeposit - amount >= parameterizer.get("minDeposit"));

        require(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;

        _Withdrawal(domain, amount, listing.currentDeposit);
    }

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

        require(msg.sender == listing.owner);
        require(isWhitelisted(domain));
        // cannot exit during ongoing challenge
        require(listing.challengeID == 0 || challengeMap[listing.challengeID].resolved);

        //remove domain & return tokens
        resetListing(domain);
    }

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

    //start a poll for a domain in the apply stage or already on the whitelist
    //tokens are taken from the challenger and the publisher's tokens are locked
    function challenge(string domain) external returns (uint challengeID) {
        bytes32 domainHash = sha3(domain);
        Listing storage listing = listingMap[domainHash];
        //to be challenged, domain must be in apply stage or already on the whitelist
        require(appExists(domain) || listing.whitelisted);
        // prevent multiple challenges
        require(listing.challengeID == 0 || challengeMap[listing.challengeID].resolved);
        uint deposit = parameterizer.get("minDeposit");
        if (listing.currentDeposit < deposit) {
            // not enough tokens, publisher auto-delisted
            resetListing(domain);
            return 0;
        }
        //take tokens from challenger
        require(token.transferFrom(msg.sender, this, deposit));
        //start poll
        uint pollID = voting.startPoll(
            parameterizer.get("voteQuorum"),
            parameterizer.get("commitPeriodLen"),
            parameterizer.get("revealPeriodLen")
        );

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

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

        _Challenge(domain, deposit, pollID);
        return pollID;
    }

    /**
    @notice updates a domain's status from application to listing, or resolves a challenge if one exists
    @param _domain The domain whose status is being updated
    */
    function updateStatus(string _domain) public {
        if (canBeWhitelisted(_domain)) {
          whitelistApplication(_domain);
          _NewDomainWhitelisted(_domain);
        } else if (challengeCanBeResolved(_domain)) {
          resolveChallenge(_domain);
        } else {
          revert();
        }
    }

    // ----------------
    // TOKEN FUNCTIONS:
    // ----------------

    // called by voter to claim reward for each completed vote
    // someone must call updateStatus() before this can be called
    function claimReward(uint _challengeID, uint _salt) public {
        // ensure voter has not already claimed tokens and challenge results have been processed
        require(tokenClaims[_challengeID][msg.sender] == false);
        require(challengeMap[_challengeID].resolved = true);

        uint voterTokens = voting.getNumPassingTokens(msg.sender, _challengeID, _salt);
        uint reward = calculateVoterReward(msg.sender, _challengeID, _salt);

        // subtract voter's information to preserve the participation ratios of other voters
        // compared to the remaining pool of rewards
        challengeMap[_challengeID].totalTokens -= voterTokens;
        challengeMap[_challengeID].rewardPool -= reward;

        require(token.transfer(msg.sender, reward));

        // ensures a voter cannot claim tokens again

        tokenClaims[_challengeID][msg.sender] = true;

        _RewardClaimed(msg.sender, _challengeID, reward);
    }

    /**
    @dev Calculate the provided voter's token reward for the given poll
    @param _voter Address of the voter whose reward balance is to be returned
    @param _challengeID pollID of the challenge a reward balance is being queried for
    @param _salt the salt for the voter's commit hash in the given poll
    @return a uint indicating the voter's reward in nano-adToken
    */
    function calculateVoterReward(address _voter, uint _challengeID, uint _salt)
    public constant returns (uint) {
        uint totalTokens = challengeMap[_challengeID].totalTokens;
        uint rewardPool = challengeMap[_challengeID].rewardPool;
        uint voterTokens = voting.getNumPassingTokens(_voter, _challengeID, _salt);
        return (voterTokens * rewardPool) / totalTokens;
    }

    // --------
    // GETTERS:
    // --------

    /**
    @dev determines whether a domain is an application which can be whitelisted
    @param _domain the domain whose status should be examined
    */
    function canBeWhitelisted(string _domain) constant public returns (bool) {
      bytes32 domainHash = sha3(_domain);
      uint challengeID = listingMap[domainHash].challengeID;

      // TODO: change name of appExists to appWasMade.
      if (appExists(_domain) && isExpired(listingMap[domainHash].applicationExpiry) &&
          !isWhitelisted(_domain) &&
          (challengeID == 0 || challengeMap[challengeID].resolved == true))
      { return true; }

      return false;
    }

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

    //return true if apply(domain) was called for this domain
    function appExists(string domain) constant public returns (bool exists) {
        return listingMap[sha3(domain)].applicationExpiry > 0;
    }

    // return true if the listing has an unresolved challenge
    function challengeExists(string _domain) constant public returns (bool) {
        bytes32 domainHash = sha3(_domain);
        uint challengeID = listingMap[domainHash].challengeID;

        return (listingMap[domainHash].challengeID > 0 && !challengeMap[challengeID].resolved);
    }

    /**
    @notice determines whether voting has concluded in a challenge for a given domain. Throws if no challenge exists.
    @param _domain a domain with an unresolved challenge
    */
    function challengeCanBeResolved(string _domain) constant public returns (bool) {
        bytes32 domainHash = sha3(_domain);
        uint challengeID = listingMap[domainHash].challengeID;

        require(challengeExists(_domain));

        return voting.pollEnded(challengeID);
    }

    //return true if termDate has passed
    function isExpired(uint termDate) constant public returns (bool expired) {
        return termDate < block.timestamp;
    }

    //delete listing from whitelist and return tokens to owner
    function resetListing(string domain) internal {
        bytes32 domainHash = sha3(domain);
        Listing storage listing = listingMap[domainHash];
        //transfer any remaining balance back to the owner
        if (listing.currentDeposit > 0)
            require(token.transfer(listing.owner, listing.currentDeposit));
        delete listingMap[domainHash];
    }

    // ----------------
    // PRIVATE FUNCTIONS:
    // ----------------

    /**
    @dev determines the winner in a challenge, rewards them tokens, and either whitelists or de-whitelists the domain
    @param _domain a domain with an unresolved challenge
    */
    function resolveChallenge(string _domain) private {
        bytes32 domainHash = sha3(_domain);
        uint challengeID = listingMap[domainHash].challengeID;

        // winner gets back their full staked deposit, and dispensationPct*loser's stake
        uint reward = (2 * challengeMap[challengeID].stake) - challengeMap[challengeID].rewardPool;
        bool wasWhitelisted = isWhitelisted(_domain);

        if (voting.isPassed(challengeID)) { // The challenge failed
            whitelistApplication(_domain);
            listingMap[domainHash].currentDeposit += reward; // give stake back to applicant

            _ChallengeFailed(challengeID);
            if (!wasWhitelisted) { _NewDomainWhitelisted(_domain); }
        }
        else { // The challenge succeeded
            resetListing(_domain);
            require(token.transfer(challengeMap[challengeID].challenger, reward));

            _ChallengeSucceeded(challengeID);
            if (wasWhitelisted) { _ListingRemoved(_domain); }
            else { _ApplicationRemoved(_domain); }
        }

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

        // store the total tokens used for voting by the winning side for reward purposes
        challengeMap[challengeID].totalTokens =
          voting.getTotalNumberOfTokensForWinningOption(challengeID);
    }

    /**
    @dev Called by updateStatus if the applicationExpiry date passed without a challenge being made
    @param _domain the domainHash to whitelist
    */
    function whitelistApplication(string _domain) private {
        bytes32 domainHash = sha3(_domain);

        listingMap[domainHash].whitelisted = true;
    }
}

pragma solidity ^0.4.11;

import "./historical/StandardToken.sol";
import "./PLCRVoting.sol";
import "./Parameterizer.sol";

contract Registry {

    // ------
    // EVENTS
    // ------

    event _Application(string domain, uint deposit);
    event _Challenge(string domain, uint deposit, uint pollID);
    event _Deposit(string domain, uint added, uint newTotal);
    event _Withdrawal(string domain, uint withdrew, uint newTotal);
    event _NewDomainWhitelisted(string domain);
    event _ApplicationRemoved(string domain);
    event _ListingRemoved(string domain);
    event _ChallengeFailed(uint challengeID);
    event _ChallengeSucceeded(uint challengeID);
    event _RewardClaimed(address voter, uint challengeID, uint reward);

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

    struct Challenge {
        uint rewardPool;        // (remaining) pool of tokens distributed amongst winning voters
        address challenger;     // owner of Challenge
        bool resolved;          // indication of if challenge is resolved
        uint stake;             // number of tokens at risk for either party during challenge
        uint totalTokens;       // (remaining) amount of tokens used for voting by the winning side
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) public challengeMap;
    // maps domainHashes to associated listing data
    mapping(bytes32 => Listing) public listingMap;
    // maps challengeIDs and address to token claim data
    mapping(uint => mapping(address => bool)) public tokenClaims;

    // Global Variables
    StandardToken public token;
    PLCRVoting public voting;
    Parameterizer public parameterizer;

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

    function Registry(
        address _tokenAddr,
        address _paramsAddr
    ) {
        token = StandardToken(_tokenAddr);
        parameterizer = Parameterizer(_paramsAddr);
        voting = new PLCRVoting(_tokenAddr);
    }

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

    //Allow a user to start an application
    //take tokens from user and set apply stage end time
    function apply(string domain, uint amount) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));
        require(amount >= parameterizer.get("minDeposit"));

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

        //transfer tokens
        require(token.transferFrom(listing.owner, this, amount));

        //set apply stage end time
        listing.applicationExpiry = block.timestamp + parameterizer.get("applyStageLen");
        listing.currentDeposit = amount;

        _Application(domain, amount);
    }

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

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

        listing.currentDeposit += amount;

        _Deposit(domain, amount, listing.currentDeposit);
    }

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

        require(listing.owner == msg.sender);
        require(amount <= listing.currentDeposit);
        require(listing.currentDeposit - amount >= parameterizer.get("minDeposit"));

        require(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;

        _Withdrawal(domain, amount, listing.currentDeposit);
    }

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

        require(msg.sender == listing.owner);
        require(isWhitelisted(domain));
        // cannot exit during ongoing challenge
        require(listing.challengeID == 0 || challengeMap[listing.challengeID].resolved);

        //remove domain & return tokens
        resetListing(domain);
    }

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

    //start a poll for a domain in the apply stage or already on the whitelist
    //tokens are taken from the challenger and the publisher's tokens are locked
    function challenge(string domain) external returns (uint challengeID) {
        bytes32 domainHash = sha3(domain);
        Listing storage listing = listingMap[domainHash];
        //to be challenged, domain must be in apply stage or already on the whitelist
        require(appExists(domain) || listing.whitelisted);
        // prevent multiple challenges
        require(listing.challengeID == 0 || challengeMap[listing.challengeID].resolved);
        uint deposit = parameterizer.get("minDeposit");
        if (listing.currentDeposit < deposit) {
            // not enough tokens, publisher auto-delisted
            resetListing(domain);
            return 0;
        }
        //take tokens from challenger
        require(token.transferFrom(msg.sender, this, deposit));
        //start poll
        uint pollID = voting.startPoll(
            parameterizer.get("voteQuorum"),
            parameterizer.get("commitPeriodLen"),
            parameterizer.get("revealPeriodLen")
        );

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

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

        _Challenge(domain, deposit, pollID);
        return pollID;
    }

    /**
    @notice updates a domain's status from application to listing, or resolves a challenge if one exists
    @param domain The domain whose status is being updated
    */
    function updateStatus(string domain) public {
        bytes32 domainHash = sha3(domain);
        uint challengeID = listingMap[domainHash].challengeID;

        // To update a domain's status it needs an unresolved challenge, or to be an application
        require(!challengeMap[challengeID].resolved || appExists(domain));

        if (appExists(domain) &&
            isExpired(listingMap[domainHash].applicationExpiry) &&
            !isWhitelisted(domain) &&
            challengeID == 0
           ) {
            // The applicationExpiry date passed without a challenge being made
            listingMap[domainHash].whitelisted = true;
            _NewDomainWhitelisted(domain);
        } else {
            // A challenge exists on the domain
            // winner gets back their full staked deposit, and dispensationPct*loser's stake
            uint stake =
              (2 * challengeMap[challengeID].stake) - challengeMap[challengeID].rewardPool;
            bool wasWhitelisted = isWhitelisted(domain);

            if (voting.isPassed(challengeID)) { // if voting is not yet over, isPassed will throw
                // The challenge failed
                listingMap[domainHash].whitelisted = true;
                listingMap[domainHash].currentDeposit += stake; // give stake back to applicant

                _ChallengeFailed(challengeID);
                if (!wasWhitelisted) { _NewDomainWhitelisted(domain); }
            } else {
                // The challenge succeeded
                resetListing(domain);
                require(token.transfer(challengeMap[challengeID].challenger, stake));

                _ChallengeSucceeded(challengeID);
                if (wasWhitelisted) { _ListingRemoved(domain); }
                else { _ApplicationRemoved(domain); }
            }

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

            // store the total tokens used for voting by the winning side for reward purposes
            challengeMap[challengeID].totalTokens =
              voting.getTotalNumberOfTokensForWinningOption(challengeID);
        }
    }

    // ----------------
    // TOKEN FUNCTIONS:
    // ----------------

    // called by voter to claim reward for each completed vote
    // someone must call updateStatus() before this can be called
    function claimReward(uint _challengeID, uint _salt) public {
        // ensure voter has not already claimed tokens and challenge results have been processed
        require(tokenClaims[_challengeID][msg.sender] == false);
        require(challengeMap[_challengeID].resolved = true);

        uint voterTokens = voting.getNumPassingTokens(msg.sender, _challengeID, _salt);
        uint reward = calculateVoterReward(msg.sender, _challengeID, _salt);

        // subtract voter's information to preserve the participation ratios of other voters
        // compared to the remaining pool of rewards
        challengeMap[_challengeID].totalTokens -= voterTokens;
        challengeMap[_challengeID].rewardPool -= reward;

        require(token.transfer(msg.sender, reward));

        // ensures a voter cannot claim tokens again

        tokenClaims[_challengeID][msg.sender] = true;

        _RewardClaimed(msg.sender, _challengeID, reward);
    }

    /**
    @dev Calculate the provided voter's token reward for the given poll
    @param _voter Address of the voter whose reward balance is to be returned
    @param _challengeID pollID of the challenge a reward balance is being queried for
    @param _salt the salt for the voter's commit hash in the given poll
    @return a uint indicating the voter's reward in nano-adToken
    */
    function calculateVoterReward(address _voter, uint _challengeID, uint _salt)
    public constant returns (uint) {
        uint totalTokens = challengeMap[_challengeID].totalTokens;
        uint rewardPool = challengeMap[_challengeID].rewardPool;
        uint voterTokens = voting.getNumPassingTokens(_voter, _challengeID, _salt);
        return (voterTokens * rewardPool) / totalTokens;
    }

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

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

    //return true if apply(domain) was called for this domain
    function appExists(string domain) constant public returns (bool exists) {
        return listingMap[sha3(domain)].applicationExpiry > 0;
    }

    //return true if termDate has passed
    function isExpired(uint termDate) constant public returns (bool expired) {
        return termDate < block.timestamp;
    }

    //delete listing from whitelist and return tokens to owner
    function resetListing(string domain) internal {
        bytes32 domainHash = sha3(domain);
        Listing storage listing = listingMap[domainHash];
        //transfer any remaining balance back to the owner
        if (listing.currentDeposit > 0)
            require(token.transfer(listing.owner, listing.currentDeposit));
        delete listingMap[domainHash];
    }
}


just return crypted

just return crypted

pragma solidity ^0.4.4;

contract Randomized {

    address public owner;
    uint public price;

    function Randomized() {
        owner = msg.sender;
        price = 10;
    }

    struct Key {
        uint entryBlockNumber;
        bytes publicKey;
    }

    mapping (address => Key) keys;

    function setKey(bytes publicKey) payable public {
        require(msg.value >= price);
        keys[msg.sender].entryBlockNumber = block.number;
        keys[msg.sender].publicKey = publicKey;
    }

    function validate(uint seedBlockNumber, bytes seed, address sender, bytes32 crypted, bytes32 result) constant public returns (bool) {
        if (keys[sender].entryBlockNumber >= seedBlockNumber) return false;
        if (keccak256(crypted, seed) != result) return false;
        return keccak256(seed) == privatized(crypted, keys[sender].publicKey);
    }

    function privatized(bytes32 crypted, bytes publicKey) constant private returns (bytes32) {
        // Waiting for https://github.com/ethereum/EIPs/pull/198
        return crypted;
    }

}

pragma solidity ^0.4.4;

contract Randomized {

    address public owner;
    uint public price;

    function Randomized() {
        owner = msg.sender;
        price = 10;
    }

    struct Key {
        uint entryBlockNumber;
        bytes publicKey;
    }

    mapping (address => Key) keys;

    function setKey(bytes publicKey) payable public {
        require(msg.value >= price);
        keys[msg.sender].entryBlockNumber = block.number;
        keys[msg.sender].publicKey = publicKey;
    }

    function validate(uint seedBlockNumber, bytes seed, address sender, bytes32 crypted, bytes32 result) constant public returns (bool) {
        if (keys[sender].entryBlockNumber >= seedBlockNumber) return false;
        if (keccak256(crypted, seed) != result) return false;
        return keccak256(seed) == privatized(crypted, keys[sender].publicKey);
    }

    function privatized(bytes32 crypted, bytes publicKey) constant private returns (bytes32) {
        // Waiting for https://github.com/ethereum/EIPs/pull/198
        return 123;
    }

}


Refactor contract to use Ownable module

Refactor contract to use Ownable module

pragma solidity ^0.4.8;

import './zeppelin/ownership/Ownable.sol';

contract StarMarket is Ownable {

// You can use this hash to verify the csv file containing all the stars
    string public csvHash = "";

    address owner;

    string public standard = 'Stars';
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;

    uint public nextStarIndexToAssign = 0;

    bool public allStarsAssigned = false;
    uint public starsRemainingToAssign = 0;

//mapping (address => uint) public addressToStarIndex;
    mapping (uint => address) public starIndexToAddress;

/* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;

    struct Offer {
    bool isForSale;
    uint starIndex;
    address seller;
    uint minValue;          // in ether
    address onlySellTo;     // specify to sell only to a specific person
    }

    struct Bid {
    bool hasBid;
    uint starIndex;
    address bidder;
    uint value;
    }

// A record of stars that are offered for sale at a specific minimum value, and perhaps to a specific person
    mapping (uint => Offer) public starsOfferedForSale;

// A record of the highest star bid
    mapping (uint => Bid) public starBids;

    mapping (address => uint) public pendingWithdrawals;

    event Assign(address indexed to, uint256 starIndex);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event StarTransfer(address indexed from, address indexed to, uint256 starIndex);
    event StarOffered(uint indexed starIndex, uint minValue, address indexed toAddress);
    event StarBidEntered(uint indexed starIndex, uint value, address indexed fromAddress);
    event StarBidWithdrawn(uint indexed starIndex, uint value, address indexed fromAddress);
    event StarBought(uint indexed starIndex, uint value, address indexed fromAddress, address indexed toAddress);
    event StarNoLongerForSale(uint indexed starIndex);

/* Initializes contract with initial supply tokens to the creator of the contract */
    function StarMarket() payable {
    //        balanceOf[msg.sender] = initialSupply;        // Give the creator all initial tokens
        owner = msg.sender;
        totalSupply = 10000;                               // Update total supply
        starsRemainingToAssign = totalSupply;
        name = "STARS";                                     // Set the name for display purposes
        symbol = "★";                                       // Set the symbol for display purposes
        decimals = 0;                                       // Amount of decimals for display purposes
    }

    function setInitialOwner(address to, uint starIndex) onlyOwner {
        if (allStarsAssigned) throw;
        if (starIndex >= 10000) throw;
        if (starIndexToAddress[starIndex] != to) {
            if (starIndexToAddress[starIndex] != 0x0) {
                balanceOf[starIndexToAddress[starIndex]]--;
            } else {
                starsRemainingToAssign--;
            }
            starIndexToAddress[starIndex] = to;
            balanceOf[to]++;
            Assign(to, starIndex);
        }
    }

    function setInitialOwners(address[] addresses, uint[] indices) onlyOwner {
        uint n = addresses.length;
        for (uint i = 0; i < n; i++) {
            setInitialOwner(addresses[i], indices[i]);
        }
    }

    function allInitialOwnersAssigned() onlyOwner {
        allStarsAssigned = true;
    }

    function getStar(uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starsRemainingToAssign == 0) throw;
        if (starIndexToAddress[starIndex] != 0x0) throw;
        if (starIndex >= 10000) throw;
        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[msg.sender]++;
        starsRemainingToAssign--;
        Assign(msg.sender, starIndex);
    }

// Transfer ownership of a star to another user without requiring payment
    function transferStar(address to, uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        if (starsOfferedForSale[starIndex].isForSale) {
            starNoLongerForSale(starIndex);
        }
        starIndexToAddress[starIndex] = to;
        balanceOf[msg.sender]--;
        balanceOf[to]++;
        Transfer(msg.sender, to, 1);
        StarTransfer(msg.sender, to, starIndex);
    // Check for the case where there is a bid from the new owner and refund it.
    // Any other bid can stay in place.
        Bid bid = starBids[starIndex];
        if (bid.bidder == to) {
        // Kill bid and refund value
            pendingWithdrawals[to] += bid.value;
            starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        }
    }

    function starNoLongerForSale(uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(false, starIndex, msg.sender, 0, 0x0);
        StarNoLongerForSale(starIndex);
    }

    function offerStarForSale(uint starIndex, uint minSalePriceInWei) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(true, starIndex, msg.sender, minSalePriceInWei, 0x0);
        StarOffered(starIndex, minSalePriceInWei, 0x0);
    }

    function offerStarForSaleToAddress(uint starIndex, uint minSalePriceInWei, address toAddress) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(true, starIndex, msg.sender, minSalePriceInWei, toAddress);
        StarOffered(starIndex, minSalePriceInWei, toAddress);
    }

    function buyStar(uint starIndex) payable {
        if (!allStarsAssigned) throw;
        Offer offer = starsOfferedForSale[starIndex];
        if (starIndex >= 10000) throw;
        if (!offer.isForSale) throw;                // star not actually for sale
        if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) throw;  // star not supposed to be sold to this user
        if (msg.value < offer.minValue) throw;      // Didn't send enough ETH
        if (offer.seller != starIndexToAddress[starIndex]) throw; // Seller no longer owner of star

        address seller = offer.seller;

        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[seller]--;
        balanceOf[msg.sender]++;
        Transfer(seller, msg.sender, 1);

        starNoLongerForSale(starIndex);
        pendingWithdrawals[seller] += msg.value;
        StarBought(starIndex, msg.value, seller, msg.sender);

    // Check for the case where there is a bid from the new owner and refund it.
    // Any other bid can stay in place.
        Bid bid = starBids[starIndex];
        if (bid.bidder == msg.sender) {
        // Kill bid and refund value
            pendingWithdrawals[msg.sender] += bid.value;
            starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        }
    }

    function withdraw() {
        if (!allStarsAssigned) throw;
        uint amount = pendingWithdrawals[msg.sender];
    // Remember to zero the pending refund before
    // sending to prevent re-entrancy attacks
        pendingWithdrawals[msg.sender] = 0;
        msg.sender.transfer(amount);
    }

    function enterBidForStar(uint starIndex) payable {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] == 0x0) throw;
        if (starIndexToAddress[starIndex] == msg.sender) throw;
        if (msg.value == 0) throw;
        Bid existing = starBids[starIndex];
        if (msg.value <= existing.value) throw;
        if (existing.value > 0) {
        // Refund the failing bid
            pendingWithdrawals[existing.bidder] += existing.value;
        }
        starBids[starIndex] = Bid(true, starIndex, msg.sender, msg.value);
        StarBidEntered(starIndex, msg.value, msg.sender);
    }

    function acceptBidForStar(uint starIndex, uint minPrice) {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        address seller = msg.sender;
        Bid bid = starBids[starIndex];
        if (bid.value == 0) throw;
        if (bid.value < minPrice) throw;

        starIndexToAddress[starIndex] = bid.bidder;
        balanceOf[seller]--;
        balanceOf[bid.bidder]++;
        Transfer(seller, bid.bidder, 1);

        starsOfferedForSale[starIndex] = Offer(false, starIndex, bid.bidder, 0, 0x0);
        uint amount = bid.value;
        starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        pendingWithdrawals[seller] += amount;
        StarBought(starIndex, bid.value, seller, bid.bidder);
    }

    function withdrawBidForStar(uint starIndex) {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] == 0x0) throw;
        if (starIndexToAddress[starIndex] == msg.sender) throw;
        Bid bid = starBids[starIndex];
        if (bid.bidder != msg.sender) throw;
        StarBidWithdrawn(starIndex, bid.value, msg.sender);
        uint amount = bid.value;
        starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
    // Refund the bid money
        msg.sender.transfer(amount);
    }

}

pragma solidity ^0.4.8;
contract StarMarket {

// You can use this hash to verify the csv file containing all the stars
    string public csvHash = "";

    address owner;

    string public standard = 'Stars';
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;

    uint public nextStarIndexToAssign = 0;

    bool public allStarsAssigned = false;
    uint public starsRemainingToAssign = 0;

//mapping (address => uint) public addressToStarIndex;
    mapping (uint => address) public starIndexToAddress;

/* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;

    struct Offer {
    bool isForSale;
    uint starIndex;
    address seller;
    uint minValue;          // in ether
    address onlySellTo;     // specify to sell only to a specific person
    }

    struct Bid {
    bool hasBid;
    uint starIndex;
    address bidder;
    uint value;
    }

// A record of stars that are offered for sale at a specific minimum value, and perhaps to a specific person
    mapping (uint => Offer) public starsOfferedForSale;

// A record of the highest star bid
    mapping (uint => Bid) public starBids;

    mapping (address => uint) public pendingWithdrawals;

    event Assign(address indexed to, uint256 starIndex);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event StarTransfer(address indexed from, address indexed to, uint256 starIndex);
    event StarOffered(uint indexed starIndex, uint minValue, address indexed toAddress);
    event StarBidEntered(uint indexed starIndex, uint value, address indexed fromAddress);
    event StarBidWithdrawn(uint indexed starIndex, uint value, address indexed fromAddress);
    event StarBought(uint indexed starIndex, uint value, address indexed fromAddress, address indexed toAddress);
    event StarNoLongerForSale(uint indexed starIndex);

/* Initializes contract with initial supply tokens to the creator of the contract */
    function StarMarket() payable {
    //        balanceOf[msg.sender] = initialSupply;        // Give the creator all initial tokens
        owner = msg.sender;
        totalSupply = 10000;                               // Update total supply
        starsRemainingToAssign = totalSupply;
        name = "STARS";                                     // Set the name for display purposes
        symbol = "★";                                       // Set the symbol for display purposes
        decimals = 0;                                       // Amount of decimals for display purposes
    }

    function setInitialOwner(address to, uint starIndex) {
        if (msg.sender != owner) throw;
        if (allStarsAssigned) throw;
        if (starIndex >= 10000) throw;
        if (starIndexToAddress[starIndex] != to) {
            if (starIndexToAddress[starIndex] != 0x0) {
                balanceOf[starIndexToAddress[starIndex]]--;
            } else {
                starsRemainingToAssign--;
            }
            starIndexToAddress[starIndex] = to;
            balanceOf[to]++;
            Assign(to, starIndex);
        }
    }

    function setInitialOwners(address[] addresses, uint[] indices) {
        if (msg.sender != owner) throw;
        uint n = addresses.length;
        for (uint i = 0; i < n; i++) {
            setInitialOwner(addresses[i], indices[i]);
        }
    }

    function allInitialOwnersAssigned() {
        if (msg.sender != owner) throw;
        allStarsAssigned = true;
    }

    function getStar(uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starsRemainingToAssign == 0) throw;
        if (starIndexToAddress[starIndex] != 0x0) throw;
        if (starIndex >= 10000) throw;
        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[msg.sender]++;
        starsRemainingToAssign--;
        Assign(msg.sender, starIndex);
    }

// Transfer ownership of a star to another user without requiring payment
    function transferStar(address to, uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        if (starsOfferedForSale[starIndex].isForSale) {
            starNoLongerForSale(starIndex);
        }
        starIndexToAddress[starIndex] = to;
        balanceOf[msg.sender]--;
        balanceOf[to]++;
        Transfer(msg.sender, to, 1);
        StarTransfer(msg.sender, to, starIndex);
    // Check for the case where there is a bid from the new owner and refund it.
    // Any other bid can stay in place.
        Bid bid = starBids[starIndex];
        if (bid.bidder == to) {
        // Kill bid and refund value
            pendingWithdrawals[to] += bid.value;
            starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        }
    }

    function starNoLongerForSale(uint starIndex) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(false, starIndex, msg.sender, 0, 0x0);
        StarNoLongerForSale(starIndex);
    }

    function offerStarForSale(uint starIndex, uint minSalePriceInWei) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(true, starIndex, msg.sender, minSalePriceInWei, 0x0);
        StarOffered(starIndex, minSalePriceInWei, 0x0);
    }

    function offerStarForSaleToAddress(uint starIndex, uint minSalePriceInWei, address toAddress) {
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        if (starIndex >= 10000) throw;
        starsOfferedForSale[starIndex] = Offer(true, starIndex, msg.sender, minSalePriceInWei, toAddress);
        StarOffered(starIndex, minSalePriceInWei, toAddress);
    }

    function buyStar(uint starIndex) payable {
        if (!allStarsAssigned) throw;
        Offer offer = starsOfferedForSale[starIndex];
        if (starIndex >= 10000) throw;
        if (!offer.isForSale) throw;                // star not actually for sale
        if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) throw;  // star not supposed to be sold to this user
        if (msg.value < offer.minValue) throw;      // Didn't send enough ETH
        if (offer.seller != starIndexToAddress[starIndex]) throw; // Seller no longer owner of star

        address seller = offer.seller;

        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[seller]--;
        balanceOf[msg.sender]++;
        Transfer(seller, msg.sender, 1);

        starNoLongerForSale(starIndex);
        pendingWithdrawals[seller] += msg.value;
        StarBought(starIndex, msg.value, seller, msg.sender);

    // Check for the case where there is a bid from the new owner and refund it.
    // Any other bid can stay in place.
        Bid bid = starBids[starIndex];
        if (bid.bidder == msg.sender) {
        // Kill bid and refund value
            pendingWithdrawals[msg.sender] += bid.value;
            starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        }
    }

    function withdraw() {
        if (!allStarsAssigned) throw;
        uint amount = pendingWithdrawals[msg.sender];
    // Remember to zero the pending refund before
    // sending to prevent re-entrancy attacks
        pendingWithdrawals[msg.sender] = 0;
        msg.sender.transfer(amount);
    }

    function enterBidForStar(uint starIndex) payable {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] == 0x0) throw;
        if (starIndexToAddress[starIndex] == msg.sender) throw;
        if (msg.value == 0) throw;
        Bid existing = starBids[starIndex];
        if (msg.value <= existing.value) throw;
        if (existing.value > 0) {
        // Refund the failing bid
            pendingWithdrawals[existing.bidder] += existing.value;
        }
        starBids[starIndex] = Bid(true, starIndex, msg.sender, msg.value);
        StarBidEntered(starIndex, msg.value, msg.sender);
    }

    function acceptBidForStar(uint starIndex, uint minPrice) {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] != msg.sender) throw;
        address seller = msg.sender;
        Bid bid = starBids[starIndex];
        if (bid.value == 0) throw;
        if (bid.value < minPrice) throw;

        starIndexToAddress[starIndex] = bid.bidder;
        balanceOf[seller]--;
        balanceOf[bid.bidder]++;
        Transfer(seller, bid.bidder, 1);

        starsOfferedForSale[starIndex] = Offer(false, starIndex, bid.bidder, 0, 0x0);
        uint amount = bid.value;
        starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        pendingWithdrawals[seller] += amount;
        StarBought(starIndex, bid.value, seller, bid.bidder);
    }

    function withdrawBidForStar(uint starIndex) {
        if (starIndex >= 10000) throw;
        if (!allStarsAssigned) throw;
        if (starIndexToAddress[starIndex] == 0x0) throw;
        if (starIndexToAddress[starIndex] == msg.sender) throw;
        Bid bid = starBids[starIndex];
        if (bid.bidder != msg.sender) throw;
        StarBidWithdrawn(starIndex, bid.value, msg.sender);
        uint amount = bid.value;
        starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
    // Refund the bid money
        msg.sender.transfer(amount);
    }

}


burn only by address

burn only by address

pragma solidity ^0.4.11;

import "./Math.sol";
import "./Concept.sol";
import "./FathomToken.sol";

contract Assessment {
    address assessee;
    address[] assessors;

    mapping (address => State) assessorState;
    State public assessmentStage;
    enum State {
        None,
        Called,
        Confirmed,
        Committed,
        Done,
        Burned
    }

    address concept;
    address fathomToken;

    uint public endTime;
    // will keep track of timelimits for 1) latest possible time to confirm and
    // 2) earliest time to reveal
    uint public checkpoint;
    uint public size;
    uint cost;

    mapping(address => bytes32) commits;
    uint public done; //counter how many assessors have committed/revealed their score
    mapping(address => int128) scores;
    int public finalScore;

    modifier onlyConcept() {
        require(msg.sender == concept);
        _;
    }

    modifier onlyInStage(State _stage) {
        require(assessmentStage == _stage);
        _;
    }

    function Assessment(address _assessee,
                        uint _size,
                        uint _cost,
                        uint _confirmTime,
                        uint _timeLimit) {
        assessee = _assessee;
        concept = msg.sender;
        fathomToken = Concept(concept).fathomToken();

        endTime = now + _timeLimit;
        // set checkpoint to latest possible time to confirm
        checkpoint = now + _confirmTime;
        assert(checkpoint < endTime);

        size = _size;
        cost = _cost;

        FathomToken(fathomToken).notification(assessee, 0); // assesse has started an assessment
        done = 0;
    }

    // ends the assessment, refunds the assessee and all assessors who have not been burned
    function cancelAssessment() private {
        uint assesseeRefund = assessmentStage == State.Called ? cost * size : cost * assessors.length; //in later stages size can be reduced by burned assessors
        FathomToken(fathomToken).addBalance(assessee, assesseeRefund, concept);
        FathomToken(fathomToken).notification(assessee, 3); //Assessment Cancelled and you have been refunded
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] != State.Burned) {
                FathomToken(fathomToken).addBalance(assessors[i], cost, concept);
                FathomToken(fathomToken).notification(assessors[i], 3); //Assessment Cancelled and you have been refunded
            }
        }
        suicide(concept);
    }

    //adds a user to the pool eligible to accept an assessment
    function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
        if (assessor != assessee && assessorState[assessor] == State.None) {
            FathomToken(fathomToken).notification(assessor, 1); //Called As A Potential Assessor
            assessorState[assessor] = State.Called;
            return true;
        }
        else {
            return false;
        }
    }

    /*
      To recursively set the pool to draw assessors from in the assessment
      stops when the pool of potential assessors is 20 times the size of the assessment2
      @param: uint seed = the seed number for random number generation
      @param: address _concept = the concept being called from
      @param: uint num = the total number of assessors to be called
    */
    function setAssessorPool(uint seed, address _concept, uint num) onlyConcept() {
        uint numCalled = 0;
        uint membersOfConcept = Concept(_concept).getMemberLength();
        //we want to call at most half of the members of each concept
        uint maxFromThisPool = num > membersOfConcept/2 ? membersOfConcept/2 : num;
        for (uint k=0; k < maxFromThisPool; k++) {
            address randomUser = Concept(_concept).getWeightedRandomMember(seed + k);
            if (randomUser != address(0x0) && addAssessorToPool(randomUser)) {
                numCalled++;
            }
        }
        uint remaining = num - numCalled;
        if (remaining > 0) {
            for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
                setAssessorPool(seed + numCalled + i, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
            }
        }
        assessmentStage = State.Called;
    }

    // called by an assessor to confirm and stake
    function confirmAssessor() onlyInStage(State.Called) {
        // cancel if the assessment is older than 12 hours or already past its timelimit
        if (now > checkpoint){
            cancelAssessment();
            return;
        }
        if (assessorState[msg.sender] == State.Called &&
            assessors.length < size &&
            FathomToken(fathomToken).takeBalance(msg.sender, address(this), cost, concept)
            ) {
            assessors.push(msg.sender);
            assessorState[msg.sender] = State.Confirmed;
            FathomToken(fathomToken).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
        }
        if (assessors.length == size) {
            notifyAssessors(uint(State.Confirmed), 4);
            FathomToken(fathomToken).notification(assessee, 4);
            assessmentStage = State.Confirmed;
        }
    }
    //called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
    function commit(bytes32 _hash) onlyInStage(State.Confirmed) {
        if (now > endTime) {
            burnStakes();
        }
        if (assessorState[msg.sender] == State.Confirmed) {
                commits[msg.sender] = _hash;
                assessorState[msg.sender] = State.Committed;
                done++; //Increases done by 1 to help progress to the next assessment stage.
        }
        if (done == size) {
            //set checkpoint to end of 12 hour challenge period after which scores can be revealed
            checkpoint = now + 12 hours;
            notifyAssessors(uint(State.Committed), 5);
            done = 0; //Resets the done counter
            assessmentStage = State.Committed;
        }
    }

    function steal(int128 _score, string _salt, address assessor) {
        if(assessorState[assessor] == State.Committed) {
            if(commits[assessor] == sha3(_score, _salt)) {
                FathomToken(fathomToken).transfer(msg.sender, cost/2);
                assessorState[assessor] = State.Burned;
                size--;
            }
        }
    }

    //@purpose: called by assessors to reveal their own commits or others
    // must be called between 12 hours after the latest commit and 24 hours after the
    // end of the assessment. If the last commit happens during at the last possible
    // point in time (right before endtime), this period will be 12hours
    function reveal(int128 _score, string _salt) onlyInStage(State.Committed) {
        // scores can only be revealed after the challenge period has passed
        require(now > checkpoint);
        // If the time to reveal has passed, burn all unrevealed assessors
        if (now > endTime + 24 hours) {
            for (uint i = 0; i < assessors.length; i++) {
                if (assessorState[assessors[i]] == State.Committed) {
                    burnAssessor(assessors[i]);
                }
            }
        }

        if(assessorState[msg.sender] == State.Committed &&
           commits[msg.sender] == sha3(_score, _salt)) {
                    scores[msg.sender] = _score;
                    assessorState[msg.sender] = State.Done;
                    done++;
        }

        if (done == size) {
            assessmentStage = State.Done;
            calculateResult();
        }
    }

    //burns stakes of all assessors who are not committed
    function burnStakes() private {
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] == State.Confirmed) {
                burnAssessor(assessors[i]);
           }
        }
    }

    /** mark an assessor as burned, reduce size and cancel assessment
        if the size is below five.
        @param _assessor address of the assessor to be burned
    */
    function burnAssessor(address _assessor) private {
        assessorState[_assessor] = State.Burned;
        if (--size < 5) {
            cancelAssessment();
        }
    }

    function notifyAssessors(uint _state, uint _topic) private {
        for (uint i=0; i < assessors.length; i++) {
            if (uint(assessorState[assessors[i]]) == _state) {
                FathomToken(fathomToken).notification(assessors[i], _topic);
            }
        }
    }

    function calculateResult() onlyInStage(State.Done) private {
        int[] memory finalScores = new int[] (done);
        uint idx =0;
        for (uint j = 0; j < assessors.length; j++) {
            if (assessorState[assessors[j]] == State.Done) {
                finalScores[idx] = scores[assessors[j]];
                idx++;
            }
        }
        uint mad = Math.calculateMAD(finalScores);
        uint finalClusterLength;
        (finalScore, finalClusterLength) = Math.getFinalScore(finalScores, mad);
        payout(mad);
        if (finalScore > 0) {
            Concept(concept).addMember(assessee, uint(finalScore) * finalClusterLength);
        }
        FathomToken(fathomToken).notification(assessee, 7);
   }

    function payout(uint mad) onlyInStage(State.Done) private {
        uint q = 1; //INFLATION RATE
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] == State.Done) {
                uint payoutValue = Math.getPayout(Math.abs(scores[assessors[i]] - finalScore), mad, cost, q);
                FathomToken(fathomToken).transfer(assessors[i], payoutValue);
                FathomToken(fathomToken).notification(assessors[i], 6); //You  got paid!
            }
        }
    }
}

pragma solidity ^0.4.11;

import "./Math.sol";
import "./Concept.sol";
import "./FathomToken.sol";

contract Assessment {
    address assessee;
    address[] assessors;

    mapping (address => State) assessorState;
    State public assessmentStage;
    enum State {
        None,
        Called,
        Confirmed,
        Committed,
        Done,
        Burned
    }

    address concept;
    address fathomToken;

    uint public endTime;
    // will keep track of timelimits for 1) latest possible time to confirm and
    // 2) earliest time to reveal
    uint public checkpoint;
    uint public size;
    uint cost;

    mapping(address => bytes32) commits;
    uint public done; //counter how many assessors have committed/revealed their score
    mapping(address => int128) scores;
    int public finalScore;

    modifier onlyConcept() {
        require(msg.sender == concept);
        _;
    }

    modifier onlyInStage(State _stage) {
        require(assessmentStage == _stage);
        _;
    }

    function Assessment(address _assessee,
                        uint _size,
                        uint _cost,
                        uint _confirmTime,
                        uint _timeLimit) {
        assessee = _assessee;
        concept = msg.sender;
        fathomToken = Concept(concept).fathomToken();

        endTime = now + _timeLimit;
        // set checkpoint to latest possible time to confirm
        checkpoint = now + _confirmTime;
        assert(checkpoint < endTime);

        size = _size;
        cost = _cost;

        FathomToken(fathomToken).notification(assessee, 0); // assesse has started an assessment
        done = 0;
    }

    // ends the assessment, refunds the assessee and all assessors who have not been burned
    function cancelAssessment() private {
        uint assesseeRefund = assessmentStage == State.Called ? cost * size : cost * assessors.length; //in later stages size can be reduced by burned assessors
        FathomToken(fathomToken).addBalance(assessee, assesseeRefund, concept);
        FathomToken(fathomToken).notification(assessee, 3); //Assessment Cancelled and you have been refunded
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] != State.Burned) {
                FathomToken(fathomToken).addBalance(assessors[i], cost, concept);
                FathomToken(fathomToken).notification(assessors[i], 3); //Assessment Cancelled and you have been refunded
            }
        }
        suicide(concept);
    }

    //adds a user to the pool eligible to accept an assessment
    function addAssessorToPool(address assessor) onlyConcept() returns(bool) {
        if (assessor != assessee && assessorState[assessor] == State.None) {
            FathomToken(fathomToken).notification(assessor, 1); //Called As A Potential Assessor
            assessorState[assessor] = State.Called;
            return true;
        }
        else {
            return false;
        }
    }

    /*
      To recursively set the pool to draw assessors from in the assessment
      stops when the pool of potential assessors is 20 times the size of the assessment2
      @param: uint seed = the seed number for random number generation
      @param: address _concept = the concept being called from
      @param: uint num = the total number of assessors to be called
    */
    function setAssessorPool(uint seed, address _concept, uint num) onlyConcept() {
        uint numCalled = 0;
        uint membersOfConcept = Concept(_concept).getMemberLength();
        //we want to call at most half of the members of each concept
        uint maxFromThisPool = num > membersOfConcept/2 ? membersOfConcept/2 : num;
        for (uint k=0; k < maxFromThisPool; k++) {
            address randomUser = Concept(_concept).getWeightedRandomMember(seed + k);
            if (randomUser != address(0x0) && addAssessorToPool(randomUser)) {
                numCalled++;
            }
        }
        uint remaining = num - numCalled;
        if (remaining > 0) {
            for (uint i = 0; i < Concept(_concept).getParentsLength(); i++) {
                setAssessorPool(seed + numCalled + i, Concept(_concept).parents(i), remaining/Concept(_concept).getParentsLength() + 1); //Calls the remaining users from the parent concepts proportional to their size
            }
        }
        assessmentStage = State.Called;
    }

    // called by an assessor to confirm and stake
    function confirmAssessor() onlyInStage(State.Called) {
        // cancel if the assessment is older than 12 hours or already past its timelimit
        if (now > checkpoint){
            cancelAssessment();
            return;
        }
        if (assessorState[msg.sender] == State.Called &&
            assessors.length < size &&
            FathomToken(fathomToken).takeBalance(msg.sender, address(this), cost, concept)
            ) {
            assessors.push(msg.sender);
            assessorState[msg.sender] = State.Confirmed;
            FathomToken(fathomToken).notification(msg.sender, 2); //Confirmed for assessing, stake has been taken
        }
        if (assessors.length == size) {
            notifyAssessors(uint(State.Confirmed), 4);
            FathomToken(fathomToken).notification(assessee, 4);
            assessmentStage = State.Confirmed;
        }
    }
    //called by an assessor to commit a hash of their score //TODO explain in more detail what's happening
    function commit(bytes32 _hash) onlyInStage(State.Confirmed) {
        if (now > endTime) {
            burnStakes();
        }
        if (assessorState[msg.sender] == State.Confirmed) {
                commits[msg.sender] = _hash;
                assessorState[msg.sender] = State.Committed;
                done++; //Increases done by 1 to help progress to the next assessment stage.
        }
        if (done == size) {
            //set checkpoint to end of 12 hour challenge period after which scores can be revealed
            checkpoint = now + 12 hours;
            notifyAssessors(uint(State.Committed), 5);
            done = 0; //Resets the done counter
            assessmentStage = State.Committed;
        }
    }

    function steal(int128 _score, string _salt, address assessor) {
        if(assessorState[assessor] == State.Committed) {
            if(commits[assessor] == sha3(_score, _salt)) {
                FathomToken(fathomToken).transfer(msg.sender, cost/2);
                assessorState[assessor] = State.Burned;
                size--;
            }
        }
    }

    //@purpose: called by assessors to reveal their own commits or others
    // must be called between 12 hours after the latest commit and 24 hours after the
    // end of the assessment. If the last commit happens during at the last possible
    // point in time (right before endtime), this period will be 12hours
    function reveal(int128 _score, string _salt) onlyInStage(State.Committed) {
        // scores can only be revealed after the challenge period has passed
        require(now > checkpoint);
        // If the time to reveal has passed, burn all unrevealed assessors
        if (now > endTime + 24 hours) {
            for (uint i = 0; i < assessors.length; i++) {
                if (assessorState[assessors[i]] == State.Committed) {
                    burnAssessor(address(0x0), i);
                }
            }
        }

        if(assessorState[msg.sender] == State.Committed &&
           commits[msg.sender] == sha3(_score, _salt)) {
                    scores[msg.sender] = _score;
                    assessorState[msg.sender] = State.Done;
                    done++;
        }

        if (done == size) {
            assessmentStage = State.Done;
            calculateResult();
        }
    }

    //burns stakes of all assessors who are not committed
    function burnStakes() private {
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] == State.Confirmed) {
                burnAssessor(address(0x0), i);
           }
        }
    }

    /* mark an assessor as burned, reduce size and cancel assessment
      if the size is below five.
      @param _assessor address of the assessor to be burned
      @param _idx index of the assessor in the assessor array (only used if the assessor address is zero)
    */
    function burnAssessor(address _assessor, uint _idx) private {
        assessorState[_assessor != address(0x0) ? _assessor : assessors[_idx]] = State.Burned;
        if (--size < 5) {
            cancelAssessment();
        }
    }

    function notifyAssessors(uint _state, uint _topic) private {
        for (uint i=0; i < assessors.length; i++) {
            if (uint(assessorState[assessors[i]]) == _state) {
                FathomToken(fathomToken).notification(assessors[i], _topic);
            }
        }
    }

    function calculateResult() onlyInStage(State.Done) private {
        int[] memory finalScores = new int[] (done);
        uint idx =0;
        for (uint j = 0; j < assessors.length; j++) {
            if (assessorState[assessors[j]] == State.Done) {
                finalScores[idx] = scores[assessors[j]];
                idx++;
            }
        }
        uint mad = Math.calculateMAD(finalScores);
        uint finalClusterLength;
        (finalScore, finalClusterLength) = Math.getFinalScore(finalScores, mad);
        payout(mad);
        if (finalScore > 0) {
            Concept(concept).addMember(assessee, uint(finalScore) * finalClusterLength);
        }
        FathomToken(fathomToken).notification(assessee, 7);
   }

    function payout(uint mad) onlyInStage(State.Done) private {
        uint q = 1; //INFLATION RATE
        for (uint i = 0; i < assessors.length; i++) {
            if (assessorState[assessors[i]] == State.Done) {
                uint payoutValue = Math.getPayout(Math.abs(scores[assessors[i]] - finalScore), mad, cost, q);
                FathomToken(fathomToken).transfer(assessors[i], payoutValue);
                FathomToken(fathomToken).notification(assessors[i], 6); //You  got paid!
            }
        }
    }
}


Use internal counter for total funds rather than account balance

pragma solidity ^0.4.2;

// 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.

import "./vendor/wealdtech-solidity/contracts/ens/ENSReverseRegister.sol";

// Interesting parts of the ENS deed
contract Deed {
    address public owner;
    address public previousOwner;
}

// Interesting parts of the ENS registry
contract Registry {
    function owner(bytes32 _hash) constant returns (address);
}

// Interesting parts of the ENS registrar
contract Registrar {
    function transfer(bytes32 _hash, address newOwner);
    function entries(bytes32 _hash) constant returns (uint, Deed, uint, uint, uint);
}

contract DomainSale is ENSReverseRegister {
    Registrar public registrar;
    mapping (string => Sale) private sales;
    mapping (address => uint256) private balances;

    // Auction parameters
    uint256 private constant AUCTION_DURATION = 24 hours;
    uint256 private constant HIGH_BID_KICKIN = 7 days;
    uint256 private constant NORMAL_BID_INCREASE_PERCENTAGE = 10;
    uint256 private constant HIGH_BID_INCREASE_PERCENTAGE = 50;

    // Distribution of the sale funds
    uint256 private constant SELLER_SALE_PERCENTAGE = 90;
    uint256 private constant START_REFERRER_SALE_PERCENTAGE = 5;
    uint256 private constant BID_REFERRER_SALE_PERCENTAGE = 5;

    // ENS
    string private constant CONTRACT_ENS = "domainsale.eth";
    // Hex is namehash("eth")
    bytes32 private constant NAMEHASH_ETH = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;

    // Funds tracker.  Ensures we don't forget about any Ether
    // Funds held for sales (both buys and bids)
    uint256 private saleFunds = 0;
    // Funds pending withdrawal
    uint256 private withdrawalFunds = 0;
    // Total funds (cannot use balance as it can be stuffed)
    uint256 private totalFunds = 0;

    struct Sale {
        // The lowest direct purchase price that will be accepted
        uint256 price;
        // The lowest auction bid that will be accepted
        uint256 reserve;
        // The last bid on the auction.  0 if no bid has been made
        uint256 lastBid;
        // The address of the last bider on the auction.  0 if no bid has been made
        address lastBidder;
        // The timestamp when this auction started
        uint256 auctionStarted;
        // The timestamp at which this auction ends
        uint256 auctionEnds;
        // The address of the referrer who started the sale
        address startReferrer;
        // The address of the referrer who supplied the winning bid
        address bidReferrer;
    }

    //
    // Events
    //

    // Sent when a name is offered (can occur multiple times if the seller
    // changes their prices)
    event Offer(address indexed seller, string name, uint256 price, uint256 reserve);
    // Sent when a bid is placed for a name
    event Bid(address indexed bidder, string name, uint256 bid);
    // Sent when a name is transferred to a new owner
    event Transfer(address indexed seller, address indexed buyer, string name, uint256 value);
    // Sent when a sale for a name is cancelled
    event Cancel(string name);

    //
    // Modifiers
    //

    // Actions that can only be undertaken by the seller of the name.
    // The owner of the name is this contract, so we use the previous
    // owner from the deed
    modifier onlyNameSeller(string _name) {
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed.owner() == address(this));
        require(deed.previousOwner() == msg.sender);
        _;
    }

    // It is possible for a name to be invalidated, in which case the
    // owner will be reset
    modifier deedValid(string _name) {
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed != 0);
        _;
    }

    // Actions that can only be undertaken if the name sale has attracted
    // no bids.
    modifier auctionNotStarted(string _name) {
        require(sales[_name].auctionStarted == 0);
        _;
    }

    // Allow if the name can be bid upon
    modifier canBid(string _name) {
        require(sales[_name].reserve != 0);
        _;
    }

    // Allow if the name can be purchased
    modifier canBuy(string _name) {
        require(sales[_name].price != 0);
        _;
    }

    // Payables add their value to the sales funds tracker
    modifier addSaleFunds(uint256 value) {
        saleFunds += value;
        totalFunds += value;
        _;
    }

    /**
     * @dev Constructor takes the address of the ENS registry
     */
    function DomainSale(address _registry) ENSReverseRegister(_registry, CONTRACT_ENS) {
        registrar = Registrar(Registry(_registry).owner(NAMEHASH_ETH));
    }

    //
    // Accessors for sales struct
    //

    /**
     * @dev return useful information from the sale structure in one go
     */
    function sale(string _name) public constant returns (uint256, uint256, uint256, address, uint256, uint256) {
        Sale storage s = sales[_name];
        return (s.price, s.reserve, s.lastBid, s.lastBidder, s.auctionStarted, s.auctionEnds);
    }

    /**
     * @dev a flag set if this name can be purchased through auction
     */
    function isAuction(string _name) public constant returns (bool) {
        return sales[_name].reserve != 0;
    }

    /**
     * @dev a flag set if this name can be purchased outright
     */
    function isBuyable(string _name) public constant returns (bool) {
        return sales[_name].price != 0;
    }

    /**
     * @dev a flag set if the auction has started
     */
    function auctionStarted(string _name) public constant returns (bool) {
        return sales[_name].lastBid != 0;
    }

    /**
     * @dev the time at which the auction ends
     */
    function auctionEnds(string _name) public constant returns (uint256) {
        return sales[_name].auctionEnds;
    }

    /**
     * @dev minimumBid is the greater of the minimum bid or the last bid + 10%.
     *      If an auction has been going longer than 7 days then it is the last
     *      bid + 50%.
     */
    function minimumBid(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        if (s.auctionStarted == 0) {
            return s.reserve;
        } else if (s.auctionStarted + HIGH_BID_KICKIN > now) {
            return s.lastBid + s.lastBid * NORMAL_BID_INCREASE_PERCENTAGE / 100;
        } else {
            return s.lastBid + s.lastBid * HIGH_BID_INCREASE_PERCENTAGE / 100;
        }
    }

    /**
     * @dev price is the instant purchase price.
     */
    function price(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        return s.price;
    }

    /**
     * @dev The balance available for withdrawal
     */
    function balance() public constant returns (uint256) {
        return balances[msg.sender];
    }

    //
    // Operations
    //

    /**
     * @dev offer a domain for sale.
     *      The price is the price at which a domain can be purchased directly.
     *      The reserve is the initial lowest price for which a bid can be made.
     */
    function offer(string _name, uint256 _price, uint256 reserve, address referrer) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        require(_price == 0 || _price > reserve);
        require(_price != 0 || reserve != 0);
        Sale storage s = sales[_name];
        s.reserve = reserve;
        s.price = _price;
        s.startReferrer = referrer;
        Offer(msg.sender, _name, _price, reserve);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev cancel a sale for a domain.
     *      This can only happen if there have been no bids for the name.
     */
    function cancel(string _name) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        registrar.transfer(sha3(_name), msg.sender);
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev buy a domain directly
     */
    function buy(string _name, address bidReferrer) canBuy(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        Sale storage s = sales[_name];
        require(msg.value >= s.price);
        require(s.auctionStarted == 0);

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        address previousOwner = deed.previousOwner();

        // Transfer the name
        registrar.transfer(sha3(_name), msg.sender);
        Transfer(previousOwner, msg.sender, _name, msg.value);

        // Distribute funds to referrers
        transferFunds(msg.value, previousOwner, s.startReferrer, bidReferrer);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev bid for a domain
     */
    function bid(string _name, address bidReferrer) canBid(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        require(msg.value >= minimumBid(_name));

        Sale storage s = sales[_name];
        require(s.auctionStarted == 0 || now < s.auctionEnds);

        // Update the balance for the outbid bidder
        balances[s.lastBidder] += s.lastBid;
        saleFunds -= s.lastBid;
        withdrawalFunds += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        if (s.auctionStarted == 0) {
          // First bid; set the auction start
          s.auctionStarted = now;
        }
        s.lastBidder = msg.sender;
        s.lastBid = msg.value;
        s.auctionEnds = now + AUCTION_DURATION;
        s.bidReferrer = bidReferrer;
        Bid(msg.sender, _name, msg.value);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev finish an auction
     */
    function finish(string _name) deedValid(_name) public {
        Sale storage s = sales[_name];
        require(now > s.auctionEnds);

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));

        address previousOwner = deed.previousOwner();
        registrar.transfer(sha3(_name), s.lastBidder);
        Transfer(previousOwner, s.lastBidder, _name, s.lastBid);

        // Distribute funds to referrers
        transferFunds(s.lastBid, previousOwner, s.startReferrer, s.bidReferrer);

        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    /**
     * @dev withdraw any owned balance
     */
    function withdraw() public {
        uint256 amount = balances[msg.sender];
        if (amount > 0) {
            balances[msg.sender] = 0;
            withdrawalFunds -= amount;
            totalFunds -= amount;
            msg.sender.transfer(amount);
        }
    }

    /**
     * @dev Invalidate an auction if the deed is no longer active
     */
    function invalidate(string _name) public {
        // Ensure the deed has been invalidated
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed == 0);

        Sale storage s = sales[_name];

        // Update the balance for the winning bidder
        balances[s.lastBidder] += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Cancel the auction
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == totalFunds);
    }

    //
    // Internal functions
    //

    /**
     * @dev Transfer funds for a sale to the relevant parties
     */
    function transferFunds(uint256 amount, address seller, address startReferrer, address bidReferrer) internal {
        uint256 startReferrerFunds = amount * START_REFERRER_SALE_PERCENTAGE / 100;
        uint256 bidReferrerFunds = amount * BID_REFERRER_SALE_PERCENTAGE / 100;
        uint256 sellerFunds = amount - startReferrerFunds - bidReferrerFunds;
        saleFunds -= amount;
        totalFunds -= amount;
        seller.transfer(sellerFunds);
        startReferrer.transfer(startReferrerFunds);
        bidReferrer.transfer(bidReferrerFunds);
    }

    /**
     * @dev Clear the storage for a name
     */
    function clearStorage(string _name) internal {
        // Clear name records
        sales[_name] = Sale({
            reserve: 0,
            price: 0,
            lastBid:0,
            lastBidder:0,
            auctionStarted:0,
            auctionEnds:0,
            startReferrer: 0,
            bidReferrer: 0
        });
    }
}

pragma solidity ^0.4.2;

// 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.

import "./vendor/wealdtech-solidity/contracts/ens/ENSReverseRegister.sol";

// Interesting parts of the ENS deed
contract Deed {
    address public owner;
    address public previousOwner;
}

// Interesting parts of the ENS registry
contract Registry {
    function owner(bytes32 _hash) constant returns (address);
}

// Interesting parts of the ENS registrar
contract Registrar {
    function transfer(bytes32 _hash, address newOwner);
    function entries(bytes32 _hash) constant returns (uint, Deed, uint, uint, uint);
}

contract DomainSale is ENSReverseRegister {
    Registrar public registrar;
    mapping (string => Sale) private sales;
    mapping (address => uint256) private balances;

    // Auction parameters
    uint256 private constant AUCTION_DURATION = 24 hours;
    uint256 private constant HIGH_BID_KICKIN = 7 days;
    uint256 private constant NORMAL_BID_INCREASE_PERCENTAGE = 10;
    uint256 private constant HIGH_BID_INCREASE_PERCENTAGE = 50;

    // Distribution of the sale funds
    uint256 private constant SELLER_SALE_PERCENTAGE = 90;
    uint256 private constant START_REFERRER_SALE_PERCENTAGE = 5;
    uint256 private constant BID_REFERRER_SALE_PERCENTAGE = 5;

    // ENS
    string private constant CONTRACT_ENS = "domainsale.eth";
    // Hex is namehash("eth")
    bytes32 private constant NAMEHASH_ETH = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae;

    // Funds tracker.  Ensures we don't forget about any Ether
    // Funds held for sales (both buys and bids)
    uint256 private saleFunds = 0;
    // Funds pending withdrawal
    uint256 private withdrawalFunds = 0;

    struct Sale {
        // The lowest direct purchase price that will be accepted
        uint256 price;
        // The lowest auction bid that will be accepted
        uint256 reserve;
        // The last bid on the auction.  0 if no bid has been made
        uint256 lastBid;
        // The address of the last bider on the auction.  0 if no bid has been made
        address lastBidder;
        // The timestamp when this auction started
        uint256 auctionStarted;
        // The timestamp at which this auction ends
        uint256 auctionEnds;
        // The address of the referrer who started the sale
        address startReferrer;
        // The address of the referrer who supplied the winning bid
        address bidReferrer;
    }

    //
    // Events
    //

    // Sent when a name is offered (can occur multiple times if the seller
    // changes their prices)
    event Offer(address indexed seller, string name, uint256 price, uint256 reserve);
    // Sent when a bid is placed for a name
    event Bid(address indexed bidder, string name, uint256 bid);
    // Sent when a name is transferred to a new owner
    event Transfer(address indexed seller, address indexed buyer, string name, uint256 value);
    // Sent when a sale for a name is cancelled
    event Cancel(string name);

    //
    // Modifiers
    //

    // Actions that can only be undertaken by the seller of the name.
    // The owner of the name is this contract, so we use the previous
    // owner from the deed
    modifier onlyNameSeller(string _name) {
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed.owner() == address(this));
        require(deed.previousOwner() == msg.sender);
        _;
    }

    // It is possible for a name to be invalidated, in which case the
    // owner will be reset
    modifier deedValid(string _name) {
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed != 0);
        _;
    }

    // Actions that can only be undertaken if the name sale has attracted
    // no bids.
    modifier auctionNotStarted(string _name) {
        require(sales[_name].auctionStarted == 0);
        _;
    }

    // Allow if the name can be bid upon
    modifier canBid(string _name) {
        require(sales[_name].reserve != 0);
        _;
    }

    // Allow if the name can be purchased
    modifier canBuy(string _name) {
        require(sales[_name].price != 0);
        _;
    }

    // Payables add their value to the sales funds tracker
    modifier addSaleFunds(uint256 value) {
        saleFunds += value;
        _;
    }

    /**
     * @dev Constructor takes the address of the ENS registry
     */
    function DomainSale(address _registry) ENSReverseRegister(_registry, CONTRACT_ENS) {
        registrar = Registrar(Registry(_registry).owner(NAMEHASH_ETH));
    }

    //
    // Accessors for sales struct
    //

    /**
     * @dev return useful information from the sale structure in one go
     */
    function sale(string _name) public constant returns (uint256, uint256, uint256, address, uint256, uint256) {
        Sale storage s = sales[_name];
        return (s.price, s.reserve, s.lastBid, s.lastBidder, s.auctionStarted, s.auctionEnds);
    }

    /**
     * @dev a flag set if this name can be purchased through auction
     */
    function isAuction(string _name) public constant returns (bool) {
        return sales[_name].reserve != 0;
    }

    /**
     * @dev a flag set if this name can be purchased outright
     */
    function isBuyable(string _name) public constant returns (bool) {
        return sales[_name].price != 0;
    }

    /**
     * @dev a flag set if the auction has started
     */
    function auctionStarted(string _name) public constant returns (bool) {
        return sales[_name].lastBid != 0;
    }

    /**
     * @dev the time at which the auction ends
     */
    function auctionEnds(string _name) public constant returns (uint256) {
        return sales[_name].auctionEnds;
    }

    /**
     * @dev minimumBid is the greater of the minimum bid or the last bid + 10%.
     *      If an auction has been going longer than 7 days then it is the last
     *      bid + 50%.
     */
    function minimumBid(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        if (s.auctionStarted == 0) {
            return s.reserve;
        } else if (s.auctionStarted + HIGH_BID_KICKIN > now) {
            return s.lastBid + s.lastBid * NORMAL_BID_INCREASE_PERCENTAGE / 100;
        } else {
            return s.lastBid + s.lastBid * HIGH_BID_INCREASE_PERCENTAGE / 100;
        }
    }

    /**
     * @dev price is the instant purchase price.
     */
    function price(string _name) public constant returns (uint256) {
        Sale storage s = sales[_name];

        return s.price;
    }

    /**
     * @dev The balance available for withdrawal
     */
    function balance() public constant returns (uint256) {
        return balances[msg.sender];
    }

    //
    // Operations
    //

    /**
     * @dev offer a domain for sale.
     *      The price is the price at which a domain can be purchased directly.
     *      The reserve is the initial lowest price for which a bid can be made.
     */
    function offer(string _name, uint256 _price, uint256 reserve, address referrer) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        require(_price == 0 || _price > reserve);
        require(_price != 0 || reserve != 0);
        Sale storage s = sales[_name];
        s.reserve = reserve;
        s.price = _price;
        s.startReferrer = referrer;
        Offer(msg.sender, _name, _price, reserve);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    /**
     * @dev cancel a sale for a domain.
     *      This can only happen if there have been no bids for the name.
     */
    function cancel(string _name) onlyNameSeller(_name) auctionNotStarted(_name) deedValid(_name) public {
        registrar.transfer(sha3(_name), msg.sender);
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    /**
     * @dev buy a domain directly
     */
    function buy(string _name, address bidReferrer) canBuy(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        Sale storage s = sales[_name];
        require(msg.value >= s.price);
        require(s.auctionStarted == 0);

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        address previousOwner = deed.previousOwner();

        // Transfer the name
        registrar.transfer(sha3(_name), msg.sender);
        Transfer(previousOwner, msg.sender, _name, msg.value);

        // Distribute funds to referrers
        transferFunds(msg.value, previousOwner, s.startReferrer, bidReferrer);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    /**
     * @dev bid for a domain
     */
    function bid(string _name, address bidReferrer) canBid(_name) deedValid(_name) addSaleFunds(msg.value) public payable {
        require(msg.value >= minimumBid(_name));

        Sale storage s = sales[_name];
        require(s.auctionStarted == 0 || now < s.auctionEnds);

        // Update the balance for the outbid bidder
        balances[s.lastBidder] += s.lastBid;
        saleFunds -= s.lastBid;
        withdrawalFunds += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        if (s.auctionStarted == 0) {
          // First bid; set the auction start
          s.auctionStarted = now;
        }
        s.lastBidder = msg.sender;
        s.lastBid = msg.value;
        s.auctionEnds = now + AUCTION_DURATION;
        s.bidReferrer = bidReferrer;
        Bid(msg.sender, _name, msg.value);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    /**
     * @dev finish an auction
     */
    function finish(string _name) deedValid(_name) public {
        Sale storage s = sales[_name];
        require(now > s.auctionEnds);

        // Obtain the previous owner from the deed
        Deed deed;
        (,deed,,,) = registrar.entries(sha3(_name));

        address previousOwner = deed.previousOwner();
        registrar.transfer(sha3(_name), s.lastBidder);
        Transfer(previousOwner, s.lastBidder, _name, s.lastBid);

        // Distribute funds to referrers
        transferFunds(s.lastBid, previousOwner, s.startReferrer, s.bidReferrer);

        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    /**
     * @dev withdraw any owned balance
     */
    function withdraw() public {
        uint256 amount = balances[msg.sender];
        if (amount > 0) {
            balances[msg.sender] = 0;
            withdrawalFunds -= amount;
            msg.sender.transfer(amount);
        }
    }

    /**
     * @dev Invalidate an auction if the deed is no longer active
     */
    function invalidate(string _name) public {
        // Ensure the deed has been invalidated
        address deed;
        (,deed,,,) = registrar.entries(sha3(_name));
        require(deed == 0);

        Sale storage s = sales[_name];

        // Update the balance for the winning bidder
        balances[s.lastBidder] += s.lastBid;

        // As we're here, return any funds that the sender is owed
        withdraw();

        // Cancel the auction
        Cancel(_name);

        // Tidy up
        clearStorage(_name);

        // Funds tracker invariant
        assert(saleFunds + withdrawalFunds == address(this).balance);
    }

    //
    // Internal functions
    //

    /**
     * @dev Transfer funds for a sale to the relevant parties
     */
    function transferFunds(uint256 amount, address seller, address startReferrer, address bidReferrer) internal {
        uint256 startReferrerFunds = amount * START_REFERRER_SALE_PERCENTAGE / 100;
        uint256 bidReferrerFunds = amount * BID_REFERRER_SALE_PERCENTAGE / 100;
        uint256 sellerFunds = amount - startReferrerFunds - bidReferrerFunds;
        saleFunds -= amount;
        seller.transfer(sellerFunds);
        startReferrer.transfer(startReferrerFunds);
        bidReferrer.transfer(bidReferrerFunds);
    }

    /**
     * @dev Clear the storage for a name
     */
    function clearStorage(string _name) internal {
        // Clear name records
        sales[_name] = Sale({
            reserve: 0,
            price: 0,
            lastBid:0,
            lastBidder:0,
            auctionStarted:0,
            auctionEnds:0,
            startReferrer: 0,
            bidReferrer: 0
        });
    }
}