Fix code style for exponent

Fix code style for exponent

pragma solidity ^0.4.11;

import './token.sol';

/// @title Dutch auction contract - distribution of tokens using an auction.
contract DutchAuction {
    /*
     * Auction for the TKN Token.
     *
     * Terminology:
     * 1 token unit = Tei
     * 1 token = TKN = Tei * multiplier
     * multiplier set from token's number of decimals (i.e. 10 ** decimals)
     */

    /*
     * Storage
     */

    CustomToken public token;
    address public owner;
    address public wallet;

    // Price decay function parameters to be changed depending on the desired outcome

    // Starting price in WEI; i.e. 2 * 10 ** 18
    uint public price_start;

    // Divisor constant; i.e. 524880000
    uint public price_constant;

    // Divisor exponent; i.e. 3
    uint32 public price_exponent;

    // For calculating elapsed time for price
    uint public start_time;
    uint public end_time;
    uint public start_block;

    // Keep track of all ETH received in the bids
    uint public received_ether;

    // Keep track of funds claimed after auction has ended
    uint public funds_claimed;

    // Total number of Tei (TKN * multiplier) that will be auctioned
    uint public multiplier;
    uint public tokens_auctioned;

    // Wei per TKN (Tei * multiplier)
    uint public final_price;

    mapping (address => uint) public bids;

    Stages public stage;

    // TODO - remove after testing
    uint rounding_error_tokens;

    /*
     * Enums
     */
    enum Stages {
        AuctionDeployed,
        AuctionSetUp,
        AuctionStarted,
        AuctionEnded,
        TokensDistributed
    }

    /*
     * Modifiers
     */
    modifier atStage(Stages _stage) {
        require(stage == _stage);
        _;
    }

    modifier isOwner() {
        require(msg.sender == owner);
        _;
    }

    modifier isValidPayload() {
        require(msg.data.length == 0 || msg.data.length == 4 || msg.data.length == 36);
        _;
    }

    /*
     * Events
     */

    event Deployed(
        address indexed _auction,
        uint indexed _price_start,
        uint indexed _price_constant,
        uint32 _price_exponent
    );
    event Setup();
    event SettingsChanged(
        uint indexed _price_start,
        uint indexed _price_constant,
        uint32 indexed _price_exponent
    );
    event AuctionStarted(uint indexed _start_time, uint indexed _block_number);
    event BidSubmission(
        address indexed _sender,
        uint indexed _amount,
        uint indexed _missing_funds
    );
    event ClaimedTokens(address indexed _recipient, uint indexed _sent_amount);
    event AuctionEnded(uint indexed _final_price);
    event TokensDistributed();

    /*
     * Public functions
     */

    /// @dev Contract constructor function sets the starting price, divisor constant and
    /// divisor exponent for calculating the Dutch Auction price.
    /// @param _wallet Wallet address to which all contributed ETH will be forwarded.
    /// @param _price_start High price in WEI at which the auction starts.
    /// @param _price_constant Auction price divisor constant.
    /// @param _price_exponent Auction price divisor exponent.
    function DutchAuction(
        address _wallet,
        uint _price_start,
        uint _price_constant,
        uint32 _price_exponent)
        public
    {
        require(_wallet != 0x0);
        wallet = _wallet;

        owner = msg.sender;
        stage = Stages.AuctionDeployed;
        Deployed(this, _price_start, _price_constant, _price_exponent);
        changeSettings(_price_start, _price_constant, _price_exponent);
    }

    /// @dev Fallback function for the contract, which calls bid() if the auction has started.
    function () public payable atStage(Stages.AuctionStarted) {
        bid(msg.sender);
    }

    /// @notice Set `_token` as the token address to be used in the auction.
    /// @dev Setup function sets external contracts addresses.
    /// @param _token Token address.
    function setup(address _token) public isOwner atStage(Stages.AuctionDeployed) {
        require(_token != 0x0);
        token = CustomToken(_token);
        require(token.owner() == owner);
        require(token.auction_address() == address(this));

        // Get number of Tei (TKN * multiplier) to be auctioned from token auction balance
        tokens_auctioned = token.balanceOf(this);

        // Set number of tokens multiplier from token decimals
        multiplier = 10 ** uint(token.decimals());

        stage = Stages.AuctionSetUp;
        Setup();
    }

    /// @notice Set `_price_start`, `_price_constant` and `_price_exponent` as
    /// the new starting price, price divisor constant and price divisor exponent.
    /// @dev Changes auction price function parameters before auction is started.
    /// @param _price_start Updated start price.
    /// @param _price_constant Updated price divisor constant.
    /// @param _price_exponent Updated price divisor exponent.
    function changeSettings(
        uint _price_start,
        uint _price_constant,
        uint32 _price_exponent)
        public
        isOwner
    {
        require(stage == Stages.AuctionDeployed || stage == Stages.AuctionSetUp);
        require(_price_start > 0);
        require(_price_constant > 0);

        price_start = _price_start;
        price_constant = _price_constant;
        price_exponent = _price_exponent;
        SettingsChanged(price_start, price_constant, price_exponent);
    }

    /// @notice Start the auction.
    /// @dev Starts auction and sets start_time.
    function startAuction() public isOwner atStage(Stages.AuctionSetUp) {
        stage = Stages.AuctionStarted;
        start_time = now;
        start_block = block.number;
        AuctionStarted(start_time, start_block);
    }

    /// @notice Finalize the auction - sets the final price and changes the auction
    /// stage after no bids are allowed anymore.
    /// @dev Finalize auction and set the final token price.
    function finalizeAuction() public atStage(Stages.AuctionStarted)
    {
        // Missing funds should be 0 at this point
        uint missing_funds = missingFundsToEndAuction();
        require(missing_funds == 0);

        // Calculate the final price WEI / TKN = WEI / (Tei / multiplier)
        final_price = multiplier * received_ether / tokens_auctioned;

        end_time = now;
        stage = Stages.AuctionEnded;
        AuctionEnded(final_price);

        assert(final_price > 0);
    }

    /// --------------------------------- Auction Functions ------------------

    /// @notice Send `msg.value` WEI to the auction from the `msg.sender` account.
    /// @dev Allows to send a bid to the auction.
    function bid() public payable atStage(Stages.AuctionStarted) {
        bid(msg.sender);
    }

    /// @notice Send `msg.value` WEI to the auction from the `receiver` account.
    /// @dev Allows to send a bid to the auction.
    /// @param receiver Bidder account address.
    function bid(address receiver)
        public
        payable
        isValidPayload
        atStage(Stages.AuctionStarted)
    {
        require(receiver != 0x0);
        require(msg.value > 0);
        require(bids[receiver] + msg.value >= msg.value);

        uint pre_receiver_funds = bids[receiver];

        // Missing funds without the current bid value
        uint missing_funds = missingFundsToEndAuction();

        // We only allow bid values less than the missing funds to end the auction value.
        require(msg.value <= missing_funds);

        bids[receiver] += msg.value;
        received_ether += msg.value;

        // Send bid amount to wallet
        wallet.transfer(msg.value);

        BidSubmission(receiver, msg.value, missing_funds);

        assert(bids[receiver] == pre_receiver_funds + msg.value);
        assert(received_ether >= msg.value);
    }

    /// @notice Claim auction tokens for `msg.sender` after the auction has ended.
    /// @dev Claims tokens for bidder after auction. To be used if tokens can be claimed by bidders, individually.
    function claimTokens() public atStage(Stages.AuctionEnded) {
        claimTokens(msg.sender);
    }

    /// @notice Claim auction tokens for `receiver` after the auction has ended.
    /// @dev Claims tokens for bidder after auction.
    /// @param receiver Tokens will be assigned to this address if set.
    function claimTokens(address receiver)
        public
        isValidPayload
        atStage(Stages.AuctionEnded)
    {
        require(receiver != 0x0);
        require(bids[receiver] > 0);

        // Number of Tei = bidded_wei / wei_per_TKN * multiplier
        uint num = multiplier * bids[receiver] / final_price;

        // Update funds claimed with full bidded amount
        // rounding errors are included to not block the contract
        funds_claimed += bids[receiver];

        // Set receiver bid to 0 before assigning tokens
        bids[receiver] = 0;

        require(token.transfer(receiver, num));

        ClaimedTokens(receiver, num);

        // Test for a correct claimed tokens calculation
        /* TODO remove this after testing */
        uint auction_unclaimed_tokens = token.balanceOf(this);

        uint unclaimed_tokens = (received_ether - funds_claimed) * multiplier / final_price;
        unclaimed_tokens += rounding_error_tokens;

        if (auction_unclaimed_tokens != unclaimed_tokens) {
            rounding_error_tokens += 1;
            unclaimed_tokens += 1;
        }
        assert(auction_unclaimed_tokens == unclaimed_tokens);
        /* End of removable test */

        // After the last tokens are claimed, we change the auction stage
        if (funds_claimed == received_ether) {
            stage = Stages.TokensDistributed;
            TokensDistributed();
        }

        assert(token.balanceOf(receiver) >= num);
        assert(bids[receiver] == 0);
    }

    /*
     *  Private functions
     */

    /// @dev Calculates the token price (WEI / TKN) at the current timestamp
    /// during the auction; elapsed time = 0 before auction starts.
    /// Based on the provided parameters, the price does not change in the first
    /// `price_constant^(1/price_exponent)` seconds due to rounding.
    /// Rounding in `decay_rate` also produces values that increase instead of decrease
    /// in the beginning; these spikes decrease over time and are noticeable
    /// only in first hours. This should be calculated before usage.
    /// @return Returns the token price - Wei per TKN.
    function calcTokenPrice() constant private returns (uint) {
        uint elapsed;
        if (stage == Stages.AuctionStarted) {
            elapsed = now - start_time;
        }

        uint decay_rate = elapsed ** price_exponent / price_constant;
        return price_start * (1 + elapsed) / (1 + elapsed + decay_rate);
    }

    /// --------------------------------- Price Functions ----------------------

    /// @notice Get the TKN price in WEI during the auction, at the moment of
    /// calling this method. Returns `0` if auction has ended.
    /// @dev Calculates current token price.
    /// @return Returns num Wei per TKN (multiplier * Tei).
    function price() public constant returns (uint) {
        if (stage == Stages.AuctionEnded ||
            stage == Stages.TokensDistributed) {
            return 0;
        }
        return calcTokenPrice();
    }

    /// @notice Get the missing funds needed to end the auction,
    /// calculated at the current TKN price.
    /// @dev The missing funds amount necessary to end the auction at the current price.
    /// @return Returns the missing funds amount.
    function missingFundsToEndAuction() constant public returns (uint) {
        uint funds_at_price = tokens_auctioned * price() / multiplier;
        if (funds_at_price < received_ether) {
            return 0;
        }
        return funds_at_price - received_ether;
    }
}

pragma solidity ^0.4.11;

import './token.sol';

/// @title Dutch auction contract - distribution of tokens using an auction.
contract DutchAuction {
    /*
     * Auction for the TKN Token.
     *
     * Terminology:
     * 1 token unit = Tei
     * 1 token = TKN = Tei * multiplier
     * multiplier set from token's number of decimals (i.e. 10**decimals)
     */

    /*
     * Storage
     */

    CustomToken public token;
    address public owner;
    address public wallet;

    // Price decay function parameters to be changed depending on the desired outcome

    // Starting price in WEI; i.e. 2 * 10**18
    uint public price_start;

    // Divisor constant; i.e. 524880000
    uint public price_constant;

    // Divisor exponent; i.e. 3
    uint32 public price_exponent;

    // For calculating elapsed time for price
    uint public start_time;
    uint public end_time;
    uint public start_block;

    // Keep track of all ETH received in the bids
    uint public received_ether;

    // Keep track of funds claimed after auction has ended
    uint public funds_claimed;

    // Total number of Tei (TKN * multiplier) that will be auctioned
    uint public multiplier;
    uint public tokens_auctioned;

    // Wei per TKN (Tei * multiplier)
    uint public final_price;

    mapping (address => uint) public bids;

    Stages public stage;

    // TODO - remove after testing
    uint rounding_error_tokens;

    /*
     * Enums
     */
    enum Stages {
        AuctionDeployed,
        AuctionSetUp,
        AuctionStarted,
        AuctionEnded,
        TokensDistributed
    }

    /*
     * Modifiers
     */
    modifier atStage(Stages _stage) {
        require(stage == _stage);
        _;
    }

    modifier isOwner() {
        require(msg.sender == owner);
        _;
    }

    modifier isValidPayload() {
        require(msg.data.length == 0 || msg.data.length == 4 || msg.data.length == 36);
        _;
    }

    /*
     * Events
     */

    event Deployed(
        address indexed _auction,
        uint indexed _price_start,
        uint indexed _price_constant,
        uint32 _price_exponent
    );
    event Setup();
    event SettingsChanged(
        uint indexed _price_start,
        uint indexed _price_constant,
        uint32 indexed _price_exponent
    );
    event AuctionStarted(uint indexed _start_time, uint indexed _block_number);
    event BidSubmission(
        address indexed _sender,
        uint indexed _amount,
        uint indexed _missing_funds
    );
    event ClaimedTokens(address indexed _recipient, uint indexed _sent_amount);
    event AuctionEnded(uint indexed _final_price);
    event TokensDistributed();

    /*
     * Public functions
     */

    /// @dev Contract constructor function sets the starting price, divisor constant and
    /// divisor exponent for calculating the Dutch Auction price.
    /// @param _wallet Wallet address to which all contributed ETH will be forwarded.
    /// @param _price_start High price in WEI at which the auction starts.
    /// @param _price_constant Auction price divisor constant.
    /// @param _price_exponent Auction price divisor exponent.
    function DutchAuction(
        address _wallet,
        uint _price_start,
        uint _price_constant,
        uint32 _price_exponent)
        public
    {
        require(_wallet != 0x0);
        wallet = _wallet;

        owner = msg.sender;
        stage = Stages.AuctionDeployed;
        Deployed(this, _price_start, _price_constant, _price_exponent);
        changeSettings(_price_start, _price_constant, _price_exponent);
    }

    /// @dev Fallback function for the contract, which calls bid() if the auction has started.
    function () public payable atStage(Stages.AuctionStarted) {
        bid(msg.sender);
    }

    /// @notice Set `_token` as the token address to be used in the auction.
    /// @dev Setup function sets external contracts addresses.
    /// @param _token Token address.
    function setup(address _token) public isOwner atStage(Stages.AuctionDeployed) {
        require(_token != 0x0);
        token = CustomToken(_token);
        require(token.owner() == owner);
        require(token.auction_address() == address(this));

        // Get number of Tei (TKN * multiplier) to be auctioned from token auction balance
        tokens_auctioned = token.balanceOf(this);

        // Set number of tokens multiplier from token decimals
        multiplier = 10**uint(token.decimals());

        stage = Stages.AuctionSetUp;
        Setup();
    }

    /// @notice Set `_price_start`, `_price_constant` and `_price_exponent` as
    /// the new starting price, price divisor constant and price divisor exponent.
    /// @dev Changes auction price function parameters before auction is started.
    /// @param _price_start Updated start price.
    /// @param _price_constant Updated price divisor constant.
    /// @param _price_exponent Updated price divisor exponent.
    function changeSettings(
        uint _price_start,
        uint _price_constant,
        uint32 _price_exponent)
        public
        isOwner
    {
        require(stage == Stages.AuctionDeployed || stage == Stages.AuctionSetUp);
        require(_price_start > 0);
        require(_price_constant > 0);

        price_start = _price_start;
        price_constant = _price_constant;
        price_exponent = _price_exponent;
        SettingsChanged(price_start, price_constant, price_exponent);
    }

    /// @notice Start the auction.
    /// @dev Starts auction and sets start_time.
    function startAuction() public isOwner atStage(Stages.AuctionSetUp) {
        stage = Stages.AuctionStarted;
        start_time = now;
        start_block = block.number;
        AuctionStarted(start_time, start_block);
    }

    /// @notice Finalize the auction - sets the final price and changes the auction
    /// stage after no bids are allowed anymore.
    /// @dev Finalize auction and set the final token price.
    function finalizeAuction() public atStage(Stages.AuctionStarted)
    {
        // Missing funds should be 0 at this point
        uint missing_funds = missingFundsToEndAuction();
        require(missing_funds == 0);

        // Calculate the final price WEI / TKN = WEI / (Tei / multiplier)
        final_price = multiplier * received_ether / tokens_auctioned;

        end_time = now;
        stage = Stages.AuctionEnded;
        AuctionEnded(final_price);

        assert(final_price > 0);
    }

    /// --------------------------------- Auction Functions ------------------

    /// @notice Send `msg.value` WEI to the auction from the `msg.sender` account.
    /// @dev Allows to send a bid to the auction.
    function bid() public payable atStage(Stages.AuctionStarted) {
        bid(msg.sender);
    }

    /// @notice Send `msg.value` WEI to the auction from the `receiver` account.
    /// @dev Allows to send a bid to the auction.
    /// @param receiver Bidder account address.
    function bid(address receiver)
        public
        payable
        isValidPayload
        atStage(Stages.AuctionStarted)
    {
        require(receiver != 0x0);
        require(msg.value > 0);
        require(bids[receiver] + msg.value >= msg.value);

        uint pre_receiver_funds = bids[receiver];

        // Missing funds without the current bid value
        uint missing_funds = missingFundsToEndAuction();

        // We only allow bid values less than the missing funds to end the auction value.
        require(msg.value <= missing_funds);

        bids[receiver] += msg.value;
        received_ether += msg.value;

        // Send bid amount to wallet
        wallet.transfer(msg.value);

        BidSubmission(receiver, msg.value, missing_funds);

        assert(bids[receiver] == pre_receiver_funds + msg.value);
        assert(received_ether >= msg.value);
    }

    /// @notice Claim auction tokens for `msg.sender` after the auction has ended.
    /// @dev Claims tokens for bidder after auction. To be used if tokens can be claimed by bidders, individually.
    function claimTokens() public atStage(Stages.AuctionEnded) {
        claimTokens(msg.sender);
    }

    /// @notice Claim auction tokens for `receiver` after the auction has ended.
    /// @dev Claims tokens for bidder after auction.
    /// @param receiver Tokens will be assigned to this address if set.
    function claimTokens(address receiver)
        public
        isValidPayload
        atStage(Stages.AuctionEnded)
    {
        require(receiver != 0x0);
        require(bids[receiver] > 0);

        // Number of Tei = bidded_wei / wei_per_TKN * multiplier
        uint num = multiplier * bids[receiver] / final_price;

        // Update funds claimed with full bidded amount
        // rounding errors are included to not block the contract
        funds_claimed += bids[receiver];

        // Set receiver bid to 0 before assigning tokens
        bids[receiver] = 0;

        require(token.transfer(receiver, num));

        ClaimedTokens(receiver, num);

        // Test for a correct claimed tokens calculation
        /* TODO remove this after testing */
        uint auction_unclaimed_tokens = token.balanceOf(this);

        uint unclaimed_tokens = (received_ether - funds_claimed) * multiplier / final_price;
        unclaimed_tokens += rounding_error_tokens;

        if (auction_unclaimed_tokens != unclaimed_tokens) {
            rounding_error_tokens += 1;
            unclaimed_tokens += 1;
        }
        assert(auction_unclaimed_tokens == unclaimed_tokens);
        /* End of removable test */

        // After the last tokens are claimed, we change the auction stage
        if (funds_claimed == received_ether) {
            stage = Stages.TokensDistributed;
            TokensDistributed();
        }

        assert(token.balanceOf(receiver) >= num);
        assert(bids[receiver] == 0);
    }

    /*
     *  Private functions
     */

    /// @dev Calculates the token price (WEI / TKN) at the current timestamp
    /// during the auction; elapsed time = 0 before auction starts.
    /// Based on the provided parameters, the price does not change in the first
    /// `price_constant^(1/price_exponent)` seconds due to rounding.
    /// Rounding in `decay_rate` also produces values that increase instead of decrease
    /// in the beginning; these spikes decrease over time and are noticeable
    /// only in first hours. This should be calculated before usage.
    /// @return Returns the token price - Wei per TKN.
    function calcTokenPrice() constant private returns (uint) {
        uint elapsed;
        if (stage == Stages.AuctionStarted) {
            elapsed = now - start_time;
        }

        uint decay_rate = elapsed**price_exponent / price_constant;
        return price_start * (1 + elapsed) / (1 + elapsed + decay_rate);
    }

    /// --------------------------------- Price Functions ----------------------

    /// @notice Get the TKN price in WEI during the auction, at the moment of
    /// calling this method. Returns `0` if auction has ended.
    /// @dev Calculates current token price.
    /// @return Returns num Wei per TKN (multiplier * Tei).
    function price() public constant returns (uint) {
        if (stage == Stages.AuctionEnded ||
            stage == Stages.TokensDistributed) {
            return 0;
        }
        return calcTokenPrice();
    }

    /// @notice Get the missing funds needed to end the auction,
    /// calculated at the current TKN price.
    /// @dev The missing funds amount necessary to end the auction at the current price.
    /// @return Returns the missing funds amount.
    function missingFundsToEndAuction() constant public returns (uint) {
        uint funds_at_price = tokens_auctioned * price() / multiplier;
        if (funds_at_price < received_ether) {
            return 0;
        }
        return funds_at_price - received_ether;
    }
}


contracts/KetherSortition.sol: Use enums for state transitions

//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

interface IKetherHomepage {
  function ads(uint _idx) external view returns (address,uint,uint,uint,uint,string memory,string memory,string memory,bool,bool);
  function getAdsLength() view external returns (uint);
}

interface IERC721 {
  function ownerOf(uint256) external view returns (address);
  function balanceOf(address) external view returns (uint256);
  function tokenOfOwnerByIndex(address, uint256) external view returns (uint256);
}

interface IERC20 {
  function balanceOf(address account) external view returns (uint256);
  function transfer(address recipient, uint256 amount) external returns (bool);
}

library Errors {
  string constant MustHaveBalance = "must have balance to nominate";
  string constant OnlyMagistrate = "only active magistrate can do this";
  string constant MustHaveEntropy = "election not executed";
  string constant MustHaveNominations = "must have nominations";
  string constant AlreadyStarted = "election already started";
  string constant AlreadyExecuted = "election already executed";
  string constant TermNotExpired = "term has not expired";
  string constant NotEnoughLink = "not enough LINK";
}

// TODO: Emit events.

contract KetherSortition is Ownable, VRFConsumerBase {
  // MagistrateToken is tokenId of an NFT whose owner controls the royalties purse for this term.
  uint256 public magistrateToken;

  uint256 constant MIN_ELECTION_DURATION = 2 days;
  uint256 constant TERM_DURATION = 6 weeks;

  uint256 public termStarted;
  uint256 public termExpires;
  uint256 public termNumber = 0;

  IERC721 ketherNFTContract;
  IKetherHomepage ketherContract;

  uint256[] public nominatedTokens;
  uint256 public nominatedPixels = 0;
  mapping(uint256 => uint256) nominations; // mapping of tokenId => termNumber
  // TODO: mapping(uint256 => uint256) electedCount;

  uint256 public electionEntropy; // Provided by Chainlink

  // nominating -[term expired & startElection() calls]> waitingForEntropy -[Chainlink calls into fullfillrandomness()]> gotEntropy -[completeElection()] -> nominating
  enum StateMachine { NOMINATING, WAITING_FOR_ENTROPY, GOT_ENTROPY }
  StateMachine state = StateMachine.NOMINATING;

  // Chainlink values
  bytes32 private s_keyHash;
  uint256 private s_fee;

  constructor(address _ketherNFTContract, address _ketherContract, address vrfCoordinator, address link, bytes32 keyHash, uint256 fee)
  public
  VRFConsumerBase(vrfCoordinator, link) {
    s_keyHash = keyHash;
    s_fee = fee;

    ketherNFTContract = IERC721(_ketherNFTContract);
    ketherContract = IKetherHomepage(_ketherContract);

    termExpires = block.timestamp + MIN_ELECTION_DURATION;
  }

  // Internal helpers:

  /**
   * @dev Only callable by Chainlink VRF, async triggered via startElection().
   */
  function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    require(state == StateMachine.WAITING_FOR_ENTROPY, Errors.AlreadyExecuted);
    electionEntropy = randomness;
    state = StateMachine.GOT_ENTROPY;
  }

  // Views:

  function getMagistrate() public view returns (address) {
    return getAdOwner(magistrateToken);
  }

  function getAdOwner(uint256 _idx) public view returns (address) {
    return ketherNFTContract.ownerOf(_idx);
  }

  function getAdPixels(uint256 _idx) public view returns (uint256) {
    (,,,uint width,uint height,,,,,) = ketherContract.ads(_idx);
    return width * height * 10;
  }

  function isNominated(uint256 _idx) public view returns (bool) {
    return nominations[_idx] > termNumber;
  }

  function getNextMagistrateToken() public view returns (uint256) {
    require(state == StateMachine.GOT_ENTROPY, Errors.MustHaveEntropy);
    require(nominatedTokens.length > 0, Errors.MustHaveNominations);

    uint256 pixelChosen = electionEntropy % nominatedPixels;
    uint256 curPixel = 0;

    for(uint256 i = 0; i < nominatedTokens.length; i++) {
      uint256 idx = nominatedTokens[i];
      curPixel += getAdPixels(idx);
      if (curPixel > pixelChosen) {
        return idx;
      }
    }
    return 0;
  }

  // External interface:

  /**
   * @dev Nominate tokens held by the sender as candidates for magistrate in the next term.
   *      Nominations of tokens are independent of their owner.
   * @return Number of nominated pixels
   */
  function nominateSelf() external returns (uint256) {
    require(state == StateMachine.NOMINATING, Errors.AlreadyStarted);
    address sender = _msgSender();
    require(ketherNFTContract.balanceOf(sender) > 0, Errors.MustHaveBalance);

    uint256 pixels = 0;
    for (uint256 i = 0; i < ketherNFTContract.balanceOf(sender); i++) {
      uint256 idx = ketherNFTContract.tokenOfOwnerByIndex(sender, i);
      if (!isNominated(idx)) {
        pixels += getAdPixels(idx);
        nominations[idx] = termNumber + 1;
        nominatedTokens.push(idx);
        // TODO: emit nominated event
      }
    }

    nominatedPixels += pixels;
    return pixels;
  }

  // TODO: Do we want nominateOther, so people can easily delegate their nominations?

  // TODO: Do we want to have a unNominateSelf()

  /**
   * @dev Stop accepting nominations, start election.
   */
  function startElection() external {
    // FIXME: check that term expired
    require(state == StateMachine.NOMINATING, Errors.AlreadyExecuted);
    require(termExpires <= block.timestamp, Errors.TermNotExpired);
    require(nominatedTokens.length > 0, Errors.MustHaveNominations);
    require(LINK.balanceOf(address(this)) >= s_fee, Errors.NotEnoughLink);

    // TODO: check if this is a re-entry vector
    state = StateMachine.WAITING_FOR_ENTROPY;
    requestRandomness(s_keyHash, s_fee);
  }

  /**
   * @dev Assign new magistrate and open up for nominations for next election.
   */
  function completeElection() external {
    require(state == StateMachine.GOT_ENTROPY, Errors.MustHaveEntropy);
    magistrateToken = getNextMagistrateToken();

    // FIXME: This is going to stagger terms, do we want to align them to
    // royalty payouts? Or maybe we could assign payouts to terms on a payment
    // receiver.
    termNumber += 1;
    termStarted = block.timestamp;
    termExpires = termStarted + TERM_DURATION;

    delete nominatedTokens;
    nominatedPixels = 0;
    state = StateMachine.NOMINATING;
  }

  // Only magistrate:

  /// @dev Transfer balance controlled by magistrate.
  function withdraw(address payable to) public {
    require(_msgSender() == getMagistrate(), Errors.OnlyMagistrate);
    // NOTE: you have exclusive rights to withdraw until the end of your term.
    // afterwards it becomes a race as someone call call an election.

    // FIXME: Would it be fun if this required having a >2 LINK balance to
    // withdraw? If we wanna be super cute, could automagically buy LINK from
    // the proceeds before transferring the remaining balance.

    to.transfer(address(this).balance);
  }

  /// @dev Cut the term short, leaving enough time for new nominations.
  function stepDown() public {
    require(_msgSender() == getMagistrate(), Errors.OnlyMagistrate);

    uint256 timeRemaining = termExpires - block.timestamp;
    if (timeRemaining > MIN_ELECTION_DURATION) {
      termExpires = block.timestamp + MIN_ELECTION_DURATION;
    }

    // TODO: Emit event
  }

  // Only owner (admin helpers):

  // TODO: adminUpdateChainlinkValues(bytes32 keyHash, uint256 fee) external onlyOwner

  function adminDelayNextEpoch(uint256 nextEpoch) external onlyOwner {
    // XXX: Implement this.
    // TODO: Should this also allow adjusting the interval?
  }

  /**
   * @dev Withdraw ERC20 tokens, primarily for rescuing remaining LINK once the
   *      experiment is over.
   */
  function adminWithdrawToken(IERC20 token, address to) external onlyOwner {
    // XXX: Add require to confirm that the contract is suspended?
    token.transfer(to, token.balanceOf(address(this)));
  }
}

//SPDX-License-Identifier: MIT
pragma solidity >=0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";

import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

interface IKetherHomepage {
  function ads(uint _idx) external view returns (address,uint,uint,uint,uint,string memory,string memory,string memory,bool,bool);
  function getAdsLength() view external returns (uint);
}

interface IERC721 {
  function ownerOf(uint256) external view returns (address);
  function balanceOf(address) external view returns (uint256);
  function tokenOfOwnerByIndex(address, uint256) external view returns (uint256);
}

interface IERC20 {
  function balanceOf(address account) external view returns (uint256);
  function transfer(address recipient, uint256 amount) external returns (bool);
}

library Errors {
  string constant MustHaveBalance = "must have balance to nominate";
  string constant OnlyMagistrate = "only active magistrate can do this";
  string constant MustHaveEntropy = "election not executed";
  string constant MustHaveNominations = "must have nominations";
  string constant AlreadyStarted = "election already started";
  string constant AlreadyExecuted = "election already executed";

  // TODO: deadcode
  string constant AlreadyNominated = "address already nominated";
  string constant TermNotExpired = "term has not expired";

  string constant NotEnoughLink = "not enough LINK";
}

// TODO: Emit events.

contract KetherSortition is Ownable, VRFConsumerBase {
  // MagistrateToken is tokenId of an NFT whose owner controls the royalties purse for this term.
  uint256 public magistrateToken;

  uint256 constant MIN_ELECTION_DURATION = 2 days;
  uint256 constant TERM_DURATION = 6 weeks;

  uint256 public termStarted;
  uint256 public termExpires;
  uint256 public termNumber = 0;

  IERC721 ketherNFTContract;
  IKetherHomepage ketherContract;

  uint256[] public nominatedTokens;
  uint256 public nominatedPixels = 0;
  mapping(uint256 => uint256) nominations; // mapping of tokenId => termNumber
  // TODO: mapping(uint256 => uint256) electedCount;

  uint256 public electionEntropy; // Provided by Chainlink

  uint8 constant STATE_NOMINATING = 0;
  uint8 constant STATE_WAITING_FOR_ENTROPY = 1;
  uint8 constant STATE_GOT_ENTROPY = 2;
  uint8 state = 0;

  // nominating -[term expired & startElection() calls]> waitingForEntropy -[Chainlink calls into fullfillrandomness()]> gotEntropy -[completeElection()] -> nominating

  // Chainlink values
  bytes32 private s_keyHash;
  uint256 private s_fee;

  constructor(address _ketherNFTContract, address _ketherContract, address vrfCoordinator, address link, bytes32 keyHash, uint256 fee)
  public
  VRFConsumerBase(vrfCoordinator, link) {
    s_keyHash = keyHash;
    s_fee = fee;

    ketherNFTContract = IERC721(_ketherNFTContract);
    ketherContract = IKetherHomepage(_ketherContract);

    termExpires = block.timestamp + MIN_ELECTION_DURATION;
  }

  // Internal helpers:

  /**
   * @dev Only callable by Chainlink VRF, async triggered via startElection().
   */
  function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    require(state == STATE_WAITING_FOR_ENTROPY, Errors.AlreadyExecuted);
    electionEntropy = randomness;
    state = STATE_GOT_ENTROPY;
  }

  // Views:

  function getMagistrate() public view returns (address) {
    return getAdOwner(magistrateToken);
  }

  function getAdOwner(uint256 _idx) public view returns (address) {
    return ketherNFTContract.ownerOf(_idx);
  }

  function getAdPixels(uint256 _idx) public view returns (uint256) {
    (,,,uint width,uint height,,,,,) = ketherContract.ads(_idx);
    return width * height * 10;
  }

  function isNominated(uint256 _idx) public view returns (bool) {
    return nominations[_idx] > termNumber;
  }

  function getNextMagistrateToken() public view returns (uint256) {
    require(state == STATE_GOT_ENTROPY, Errors.MustHaveEntropy);
    require(nominatedTokens.length > 0, Errors.MustHaveNominations);

    uint256 pixelChosen = electionEntropy % nominatedPixels;
    uint256 curPixel = 0;

    for(uint256 i = 0; i < nominatedTokens.length; i++) {
      uint256 idx = nominatedTokens[i];
      curPixel += getAdPixels(idx);
      if (curPixel > pixelChosen) {
        return idx;
      }
    }
  }

  // External interface:

  /**
   * @dev Nominate tokens held by the sender as candidates for magistrate in the next term.
   *      Nominations of tokens are independent of their owner.
   * @return Number of nominated pixels
   */
  function nominateSelf() external returns (uint256) {
    require(state == STATE_NOMINATING, Errors.AlreadyStarted);
    address sender = _msgSender();
    require(ketherNFTContract.balanceOf(sender) > 0, Errors.MustHaveBalance);

    uint256 pixels = 0;
    for (uint256 i = 0; i < ketherNFTContract.balanceOf(sender); i++) {
      uint256 idx = ketherNFTContract.tokenOfOwnerByIndex(sender, i);
      if (!isNominated(idx)) {
        pixels += getAdPixels(idx);
        nominations[idx] = termNumber + 1;
        nominatedTokens.push(idx);
        // TODO: emit nominated event
      }
    }

    nominatedPixels += pixels;
    return pixels;
  }

  // TODO: Do we want nominateOther, so people can easily delegate their nominations?

  // TODO: Do we want to have a unNominateSelf()

  /**
   * @dev Stop accepting nominations, start election.
   */
  function startElection() external {
    // FIXME: check that term expired
    require(state == STATE_NOMINATING, Errors.AlreadyExecuted);
    require(termExpires <= block.timestamp, Errors.TermNotExpired);
    require(nominatedTokens.length > 0, Errors.MustHaveNominations);
    require(LINK.balanceOf(address(this)) >= s_fee, Errors.NotEnoughLink);

    // TODO: check if this is a re-entry vector
    state = STATE_WAITING_FOR_ENTROPY;
    requestRandomness(s_keyHash, s_fee);
  }

  /**
   * @dev Assign new magistrate and open up for nominations for next election.
   */
  function completeElection() external {
    require(state == STATE_GOT_ENTROPY, Errors.MustHaveEntropy);
    magistrateToken = getNextMagistrateToken();

    // FIXME: This is going to stagger terms, do we want to align them to
    // royalty payouts? Or maybe we could assign payouts to terms on a payment
    // receiver.
    termNumber += 1;
    termStarted = block.timestamp;
    termExpires = termStarted + TERM_DURATION;

    delete nominatedTokens;
    nominatedPixels = 0;
    state = STATE_NOMINATING;
  }

  // Only magistrate:

  /// @dev Transfer balance controlled by magistrate.
  function withdraw(address payable to) public {
    require(_msgSender() == getMagistrate(), Errors.OnlyMagistrate);
    // NOTE: you have exclusive rights to withdraw until the end of your term.
    // afterwards it becomes a race as someone call call an election.

    // FIXME: Would it be fun if this required having a >2 LINK balance to
    // withdraw? If we wanna be super cute, could automagically buy LINK from
    // the proceeds before transferring the remaining balance.

    to.transfer(address(this).balance);
  }

  /// @dev Cut the term short, leaving enough time for new nominations.
  function stepDown() public {
    require(_msgSender() == getMagistrate(), Errors.OnlyMagistrate);

    uint256 timeRemaining = termExpires - block.timestamp;
    if (timeRemaining > MIN_ELECTION_DURATION) {
      termExpires = block.timestamp + MIN_ELECTION_DURATION;
    }

    // TODO: Emit event
  }

  // Only owner (admin helpers):

  // TODO: adminUpdateChainlinkValues(bytes32 keyHash, uint256 fee) external onlyOwner

  function adminDelayNextEpoch(uint256 nextEpoch) external onlyOwner {
    // XXX: Implement this.
    // TODO: Should this also allow adjusting the interval?
  }

  /**
   * @dev Withdraw ERC20 tokens, primarily for rescuing remaining LINK once the
   *      experiment is over.
   */
  function adminWithdrawToken(IERC20 token, address to) external onlyOwner {
    // XXX: Add require to confirm that the contract is suspended?
    token.transfer(to, token.balanceOf(address(this)));
  }
}


again?

again?

pragma solidity ^0.4.9;

contract Workflow {

// Data types:

    // enums are zero indexed - FYI.
    enum RelationType {
        Include, Exclude, Response, Condition, Milestone
    }

    struct Activity {
        // State
        bytes32 name;
        bool included;
        bool executed;
        bool pending;

        // Outgoing relations:
        uint32[] include;
        uint32[] exclude;
        uint32[] response;

        // Incoming relations:
        uint32[] condition;
        uint32[] milestone;

        // Access rights:
        address[] whitelist;
    }

// Workflow data and creation:
    event Execution(address sender, uint32 id, bool successful);

    Activity[] activities;
    address owner;
    bytes32 name;

    /// names: array of activity names, where names[i] is the name of activity with id i
    /// included: array of activity included state, where included[i] is the included state of activity with id i
    /// executed: array of activity executed state, where executed[i] is the executed state of activity with id i
    /// pending: array of activity pending state, where pending[i] is the pending state of activity with id i
    function Workflow (
        bytes32 wfName,
        bytes32[] names,
        bool[] included,
        bool[] executed,
        bool[] pending,

        uint32[] relations,
        RelationType[] relationType,

        address[] authorizedUsers, //order is defined by numberOfAuths
        uint32[] numberOfAuths //numberOfAuths[0] = number of authorized users for activity 0. If 0, anyone is authorized.
    ) {
        if((relations.length/2) != relationType.length)
            throw;

        name = wfName;
        owner = msg.sender;

        activities.length = names.length;
        var authIndex = 0;

        for (var i = 0; i < names.length; i++) {
            activities[i].name      = names[i];
            activities[i].included  = included[i];
            activities[i].executed  = executed[i];
            activities[i].pending   = pending[i];
            activities[i].whitelist.length = numberOfAuths[i];
            for(var j = 0; j < numberOfAuths[i]; j++){
                activities[i].whitelist[j] = authorizedUsers[authIndex++];
            }
        }
        // We expect the 'relations' array is formed such the first index is the FROM activity and the next index is the TO activity. Rinse and repeat.
        for(i = 0; i < relations.length; i=i+2){
            if(relationType[i/2] == RelationType.Include)        activities[relations[i]].include.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Exclude)   activities[relations[i]].exclude.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Response)  activities[relations[i]].response.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Condition) activities[relations[i+1]].condition.push(relations[i]);
            else if(relationType[i/2] == RelationType.Milestone) activities[relations[i+1]].milestone.push(relations[i]);
            else throw;
        }

    }

// Internal functions:
    function authorize(uint32 actId) private {
        if(activities[actId].whitelist.length > 0) {
            for (var i = 0; i < activities[actId].whitelist.length; i++)
                if(activities[actId].whitelist[i] == msg.sender)
                    break;

            if(i == activities[actId].whitelist.length){
                throw;
            }
        }
    }

// External functions:
    function execute(uint32 id) {
        // Check that activity is in executable state
        authorize(id);
        Activity memory a = activities[id];

        if(!a.included){
            throw;
        }

        for (var i = 0; i < a.condition.length; i++)
            if(!activities[a.condition[i]].executed && activities[a.condition[i]].included) {
                throw;
                return;
            }

        for (i = 0; i < a.milestone.length; i++)
            if(activities[a.milestone[i]].pending && activities[a.milestone[i]].included) {
                throw;
                return;
            }

        a.executed = true;
        a.pending = false;

        // Effects on other activities
        for (i = 0; i < a.include.length; i++)
            activities[a.include[i]].included = true;
        for (i = 0; i < a.exclude.length; i++)
            activities[a.exclude[i]].included = false;
        for (i = 0; i < a.response.length; i++)
            activities[a.response[i]].pending = true;

        Execution(msg.sender, id, true);
    }

    // Selfdestruct
    function sudoku() {
        if(msg.sender != owner)
            throw;

        selfdestruct(owner);
    }
    function isIncluded(uint i) public constant returns (bool){
        return activities[i].included;
    }
    function isPending(uint i) public constant returns (bool){
        return activities[i].pending;
    }
    function isExecuted(uint i) public constant returns (bool){
        return activities[i].executed;
    }
    function getIncludeRelations(uint i) public constant returns (uint32[]){
        return activities[i].include;
    }
    function getExcludeRelations(uint i) public constant returns (uint32[]){
        return activities[i].exclude;
    }
    function getResponseRelations(uint i) public constant returns (uint32[]){
        return activities[i].response;
    }
    function getConditionRelations(uint i) public constant returns (uint32[]){
        return activities[i].condition;
    }
    function getMilestoneRelations(uint i) public constant returns (uint32[]){
        return activities[i].milestone;
    }
}

pragma solidity ^0.4.9;

contract Workflow {

// Data types:

    // enums are zero indexed - FYI.
    enum RelationType {
        Include, Exclude, Response, Condition, Milestone
    }

    struct Activity {
        // State
        bytes32 name;
        bool included;
        bool executed;
        bool pending;

        // Outgoing relations:
        uint32[] include;
        uint32[] exclude;
        uint32[] response;

        // Incoming relations:
        uint32[] condition;
        uint32[] milestone;

        // Access rights:
        address[] whitelist;
    }

// Workflow data and creation:
    event Execution(address sender, uint32 id, bool successful);

    Activity[] activities;
    address owner;
    bytes32 name;

    /// names: array of activity names, where names[i] is the name of activity with id i
    /// included: array of activity included state, where included[i] is the included state of activity with id i
    /// executed: array of activity executed state, where executed[i] is the executed state of activity with id i
    /// pending: array of activity pending state, where pending[i] is the pending state of activity with id i
    function Workflow (
        bytes32 wfName,
        bytes32[] names,
        bool[] included,
        bool[] executed,
        bool[] pending,

        uint32[] relations,
        RelationType[] relationType,

        address[] authorizedUsers, //order is defined by numberOfAuths
        uint32[] numberOfAuths //numberOfAuths[0] = number of authorized users for activity 0. If 0, anyone is authorized.
    ) {
        if((relations.length/2) != relationType.length)
            throw;

        name = wfName;
        owner = msg.sender;

        activities.length = names.length;
        var authIndex = 0;

        for (var i = 0; i < names.length; i++) {
            activities[i].name      = names[i];
            activities[i].included  = included[i];
            activities[i].executed  = executed[i];
            activities[i].pending   = pending[i];
            activities[i].whitelist.length = numberOfAuths[i];
            for(var j = 0; j < numberOfAuths[i]; j++){
                activities[i].whitelist[j] = authorizedUsers[authIndex++];
            }
        }
        // We expect the 'relations' array is formed such the first index is the FROM activity and the next index is the TO activity. Rinse and repeat.
        for(i = 0; i < relations.length; i=i+2){
            if(relationType[i/2] == RelationType.Include)        activities[relations[i]].include.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Exclude)   activities[relations[i]].exclude.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Response)  activities[relations[i]].response.push(relations[i+1]);
            else if(relationType[i/2] == RelationType.Condition) activities[relations[i+1]].condition.push(relations[i]);
            else if(relationType[i/2] == RelationType.Milestone) activities[relations[i+1]].milestone.push(relations[i]);
            else throw;
        }

    }

// Internal functions:
    function authorize(uint32 actId) private {
        if(activities[actId].whitelist.length > 0) {
            for (var i = 0; i < activities[actId].whitelist.length; i++)
                if(activities[actId].whitelist[i] == msg.sender)
                    break;

            if(i == activities[actId].whitelist.length){
                throw;
            }
        }
    }

// External functions:
    function execute(uint32 id) {
        // Check that activity is in executable state
        authorize(id);
        Activity memory a = activities[id];

        if(!a.included){
            throw;
        }

        for (var i = 0; i < a.condition.length; i++)
            if(!activities[a.condition[i]].executed && activities[a.condition[i]].included) {
                throw;
                return;
            }

        for (i = 0; i < a.milestone.length; i++)
            if(activities[a.milestone[i]].pending && activities[a.milestone[i]].included) {
                throw;
                return;
            }

        a.executed = true;
        a.pending = false;

        // Effects on other activities
        for (i = 0; i < a.include.length; i++)
            activities[a.include[i]].included = true;
        for (i = 0; i < a.exclude.length; i++)
            activities[a.exclude[i]].included = false;
        for (i = 0; i < a.response.length; i++)
            activities[a.response[i]].pending = true;

        Execution(msg.sender, id, true);
    }

    // Selfdestruct
    function sudoku() {
        if(msg.sender != owner)
            throw;

        selfdestruct(owner);
    }
}


Add comments

Add comments

contract Master
{
  function Master()
  {

  }
  //Initialize master data store
  mapping(address => uint) tokenBalance;
  mapping(address => string) tagName;
  mapping(string => address) tagAddress;
  mapping(address => string[]) acheivements;

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

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parents; //The tags this is a subset of
  address masterAddress;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  function Tag()
  {

  }
  function addParent(address parent)
  {
    parents.push(parent);
  }
  function setMaster(address master)
  {
    masterAddress = master;
  }
  function getAssessors(uint randomNumber)
  {

  }
  function startAssessment(address assessee, address[] assessors)
  {
    Assessment newAssessment;
    newAssessment.setAssessee(assessee);
    newAssessment.setAssessors(assessors);
  }
  function getAssessmentResults(bool result, uint score, address assessee, address assessment) returns(bool)
  {
    if(result == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
    }
    assessmentHistory[assessee] = assessment;
    return result;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessors;
  mapping(address => uint[]) assessmentData; //Given by the assessors as IPFS hashes
  mapping(address => uint[]) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => bool[]) assessmentResults; //Pass/Fail given by assessors
  mapping(address => uint[]) assessmentScores; //Numerical score given by assessors

  function Assessment()
  {

  }
  function setAssessee(address newAssessee)
  {
    assessee = newAssessee;
  }
  function setAssessors(address[] newAssessors)
  {
    assessors = newAssessors;
  }
  function assess()
  {

  }
  function cashout()
  {

  }
}

contract Master
{
  function Master()
  {

  }
  //Initialize master data store
  mapping(address => uint) tokenBalance;
  mapping(address => string) tagName;
  mapping(string => address) tagAddress;
  mapping(address => string[]) acheivements;

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

//Defines the meta-contract for a Tag
contract Tag
{
  address[] parents; //The tags this is a subset of
  address masterAddress;
  address[] owners; //Those who have earned the tag
  mapping(address => address[]) assessmentHistory; //All assessments completed
  mapping(address => uint) scores; //All positive assessements scores
  function Tag()
  {

  }
  function addParent(address parent)
  {
    parents.push(parent);
  }
  function setMaster(address master)
  {
    masterAddress = master;
  }
  function getAssessors(uint randomNumber)
  {

  }
  function startAssessment(address assessee, address[] assessors)
  {
    Assessment newAssessment;
    newAssessment.setAssessee(assessee);
    newAssessment.setAssessors(assessors);
  }
  function getAssessmentResults(bool result, uint score, address assessee, address assessment) returns(bool)
  {
    if(result == true)
    {
      owners.push(assessee);
      scores[assessee] = score;
    }
    assessmentHistory[assessee] = assessment;
    return result;
  }
}

//Defines the meta-contract for an assessment
contract Assessment
{
  address assessee; //We need a better word for this
  address[] assessors;
  mapping(address => uint[]) assessmentData; //Given by the assessors as IPFS hashes
  mapping(address => uint[]) assessmentAnswers; //Given by the assessee as IPFS hashes
  mapping(address => bool[]) assessmentResults;
  mapping(address => uint[]) assessmentScores;

  function Assessment()
  {

  }
  function setAssessee(address newAssessee)
  {
    assessee = newAssessee;
  }
  function setAssessors(address[] newAssessors)
  {
    assessors = newAssessors;
  }
  function assess()
  {

  }
  function cashout()
  {

  }
}


Update Registry.sol

Update Registry.sol

pragma solidity 0.4.11;
import "./StandardToken.sol";

contract Registry {

	public address wallet;

	// this will later be the parameters from the parametrizer
	public uint expTime;
	public uint applyCost;
	public uint challengeTime;

	struct Publisher {
		address owner;
		uint time;
		uint2 status;  // is this publisher in the whitelist or applying or already challenged etc
	}

	// status description:
	// 0 = whitelisted, either expired or not based on time variable
	// 1 = new applicant waiting to be challenged
	// 2 = already been challenged
	// this can possibly just be a bool later

	mapping(bytes32 => Publisher) public domainMap;

	function Registry(address _token) {
		// set parameters somehow
		StandardToken token = StandardToken(_token);
	}

	function add(bytes32 _domainHash) private {
		domainMap[domainHash].time = now + expTime;
		domainMap[domainHash].status = 0;
	}

	function isVerified(string _domain) returns (bool) {
		bytes32 domainHash = sha3(_domain);
		if (domainMap[domainHash].time < now && domainMap[domainHash].status != 0) {
			return false;
		}
		else {
			return true;
		}
	}

	function apply(string _domain) {
		require(token.allowance(msg.sender, this) >= applyCost);
		token.transferFrom(msg.sender, wallet, applyCost);
		bytes32 domainHash = sha3(_domain);
		// if success
		domainMap[domainHash].status = 1;
		domainMap[domainHash].time = now + challengeTime;
		// trigger an event
	}

	// save challenger somewhere
	function challenge(string _domain) {
		require(token.allowance(msg.sender, this) >= applyCost);
		token.transferFrom(msg.sender, wallet, applyCost);
		bytes32 domainHash = sha3(_domain);
		require(domainMap[domainHash].status != 2); // works for both unexpired whitelisted and new applicants
		require(domainMap[domainHash].time < now);
		domainMap[domainHash].status = 2;
		if (callVote(domainHash, domainMap[domainHash].time) == true) {
			add(_domain);
		}
		else {
			token.transferFrom(mwallet, msg.sender, applyCost);
			// trigger event to notify applicant?
		}
	}

	function moveToRegistry(string _domain) {
		bytes32 domainHash = sha3(_domain);
		require(domainMap[domainHash].time > now);
		require(domainMap[domainHash].status == 1);
		add(domainHash);
	}

	// claim tokens function

	function callVote(bytes32 _domainHash, uint _time) private returns (bool) {
		// event that vote has started
		// ??
	}

	//

}

pragma solidity 0.4.11;
import "./StandardToken.sol";

contract Registry {

	public address wallet;

	// this will later be the parameters from the parametrizer
	public uint expTime;
	public uint applyCost;
	public uint challengeTime;

	struct Publisher {
		address owner;
		uint time;
		uint2 status;  // is this publisher in the whitelist or applying or already challenged etc
	}

	// status description:
	// 0 = whitelisted, either expired or not based on time variable
	// 1 = new applicant waiting to be challenged
	// 2 = already been challenged
	// this can possibly just be a bool later

	mapping(bytes32 => Publisher) public domainMap;

	function Registry(address _token) {
		// set parameters somehow
		StandardToken token = StandardToken(_token);
	}

	function add(bytes32 _domainHash) private {
		domainMap[domainHash].time = now + expTime;
		domainMap[domainHash].status = 0;
	}

	function isVerified(string _domain) returns (bool) {
		bytes32 domainHash = sha3(_domain);
		if (domainMap[domainHash].time < now && domainMap[domainHash].status != 0) {
			return false;
		}
		else {
			return true;
		}
	}

	function apply(string _domain) {
		require(token.allowance(msg.sender, this) >= applyCost);
		token.transferFrom(msg.sender, wallet, applyCost);
		bytes32 domainHash = sha3(_domain);
		// if success
		domainMap[domainHash].status = 1;
		domainMap[domainHash].time = now + challengeTime;
		// trigger an event
	}

	// save challenger somewhere
	function challenge(string _domain) {
		require(token.allowance(msg.sender, this) >= applyCost);
		token.transferFrom(msg.sender, wallet, applyCost);
		bytes32 domainHash = sha3(_domain);
		require(domainMap[domainHash].status != 2); // works for both unexpired whitelisted and new applicants
		require(domainMap[domainHash].time < now);
		domainMap[domainHash].status = 2;
		if (callVote(domainHash, domainMap[domainHash].time) == true) {
			addToRegistry(_domain);
		}
		else {
			token.transferFrom(mwallet, msg.sender, applyCost);
			// trigger event to notify applicant?
		}
	}

	function moveToRegistry(string _domain) {
		bytes32 domainHash = sha3(_domain);
		require(domainMap[domainHash].time > now);
		require(domainMap[domainHash].status == 1);
		add(domainHash);
	}

	// claim tokens function

	function callVote(bytes32 _domainHash, uint _time) private returns (bool) {
		// event that vote has started
		// ??
	}

	//

}


fix isExpired() comparison

fix isExpired() comparison

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

contract Registry {

    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;        // 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 remainder;         // remainder tokens from flooring rewards to be claimed by the winner
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) 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;
    // maps hash of parameter name to parameter value
    mapping(bytes32 => uint) public Parameters;

    // Global Variables
    StandardToken token;
    PLCRVoting voting;

    // Constants
    bytes32 constant private MINDEPOSIT_h = sha3("minDeposit");
    bytes32 constant private MINPARAMDEPOSIT_h = sha3("minParamDeposit");
    bytes32 constant private APPLYSTAGELEN_h = sha3("applyStageLen");
    bytes32 constant private COMMITPERIODLEN_h = sha3("commitPeriodLen");
    bytes32 constant private REVEALPERIODLEN_h = sha3("revealPeriodLen");
    bytes32 constant private DISPENSATIONPCT_h = sha3("dispensationPct");
    bytes32 constant private VOTEQUORUM_h = sha3("voteQuorum");
    uint256 constant private MULTIPLIER = 10 ** 18;  // used to help represent doubles as ints in token rewards

// ------------
// CONSTRUCTOR:
// ------------
    /// @param _minDeposit      minimum deposit for listing to be whitelisted
    /// @param _minParamDeposit minimum deposit to propose a parameter change
    /// @param _applyStage      length of period in which applicants wait to be whitelisted
    /// @param _dispensationPct percentage of losing party's deposit distributed to winning party
    /// @param _commitPeriodLen length of commit period for voting
    /// @param _revealPeriodLen length of reveal period for voting
    /// @param _voteQuorum      type of majority out of 100 necessary for vote success

    function Registry(
        address _tokenAddr,
        uint _minDeposit,
        uint _minParamDeposit,
        uint _applyStageLen,
        uint _commitPeriodLen,
        uint _revealPeriodLen,
        uint _dispensationPct,
        uint _voteQuorum
    ) {
        token = StandardToken(_tokenAddr);
        voting = new PLCRVoting(_tokenAddr);
        Parameters[MINDEPOSIT_h] = _minDeposit;
        Parameters[MINPARAMDEPOSIT_h] = _minParamDeposit;
        Parameters[APPLYSTAGELEN_h] = _applyStageLen;
        Parameters[DISPENSATIONPCT_h] = _dispensationPct;
        Parameters[COMMITPERIODLEN_h] = _commitPeriodLen;
        Parameters[REVEALPERIODLEN_h] = _revealPeriodLen;
        Parameters[VOTEQUORUM_h] = _voteQuorum;
    }

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

    //Allow a user to start an application
    //take tokens from user and set apply stage end time
    function apply(string domain) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));

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

        //transfer tokens
        uint minDeposit = Parameters[MINDEPOSIT_h];
        require(token.transferFrom(listing.owner, this, minDeposit));

        //set apply stage end time
        listing.applicationExpiry = block.timestamp + Parameters[APPLYSTAGELEN_h];
        listing.currentDeposit = minDeposit;
    }

    //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;
    }

    //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(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;
    }

    //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(isWhitelisted(domain));
        // cannot exit during ongoing challenge
        require(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);
        require(challengeMap[listing.challengeID].resolved); // prevent multiple challenges

        if (listing.currentDeposit < Parameters[MINDEPOSIT_h]) {
            // not enough tokens, publisher auto-delisted
            resetListing(domain);
            return 0;
        }
        //take tokens from challenger
        uint deposit = Parameters[MINDEPOSIT_h];
        require(token.transferFrom(msg.sender, this, deposit));
        //start poll
        uint pollID = voting.startPoll(domain,
            Parameters[VOTEQUORUM_h],
            Parameters[COMMITPERIODLEN_h],
            Parameters[REVEALPERIODLEN_h]
        );

        challengeMap[pollID] = Challenge({
            challenger: msg.sender,
            rewardPool: ((100 - Parameters[DISPENSATIONPCT_h]) * deposit) / 100,
            stake: deposit,
            resolved: false,
            remainder: 0
        });

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

        return pollID;
    }

    // whitelist domain if apply stage ended without a challenge
    function updateStatus(string domain) public {
        bytes32 domainHash = sha3(domain);
        uint challengeID = listingMap[domainHash].challengeID;
        require(!challengeMap[challengeID].resolved);  // require processed flag to be false

        // IF NO CHALLENGE AFTER APPLY STAGE
        if (challengeID == 0 && isExpired(listingMap[domainHash].applicationExpiry)) {
            listingMap[domainHash].whitelisted = true;
        } else {
        // PROCESS THE RESULT OF THE POLL

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

            if (voting.isPassed(challengeID)) {
                listingMap[domainHash].whitelisted = true;
                listingMap[domainHash].currentDeposit += stake; // give stake back to applicant
            } else {
                resetListing(domain); // whitelisted = false
                require(token.transfer(challengeMap[challengeID].challenger, stake)); // give stake to challenger
            }

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

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

    // called by voter to claim reward for each completed vote
    function claimReward(uint _challengeID, uint _salt) public {
        // ensure voter has not already claimed tokens
        require(tokenClaims[_challengeID][msg.sender] == false);
        uint reward = calculateTokens(_challengeID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        tokenClaims[_challengeID][msg.sender] = true;
    }

    // helper function to claimReward()
    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function calculateTokens(uint _challengeID, uint _salt, address _voter) private returns(uint) {
        uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_challengeID);
        uint256 voterTokens = voting.getNumPassingTokens(_voter, _challengeID, _salt);

        uint256 rewardPool = challengeMap[_challengeID].rewardPool * MULTIPLIER;
        uint256 numerator = voterTokens * rewardPool;
        uint256 denominator = totalTokens * MULTIPLIER;
        uint256 remainder = numerator % denominator;

        // save remainder tokens in the form of decimal numbers with 18 places represented
        // as a uint256
        challengeMap[_challengeID].remainder += remainder;

        return numerator / denominator;
    }

    // gives reminder tokens from poll to a designated claimer
    // the claimer is the winner of the challenge
    // for every poll there will be ~0.5 nano AdTokens burned,
    // since the winner cannot withdraw a decimal amount of nano AdToken
    function claimExtraReward(uint _challengeID, string _domain) public {
        // uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_challengeID);
        uint256 reward = challengeMap[_challengeID].remainder / (MULTIPLIER);
        // reward = reward / totalTokens; // should this be here?
        challengeMap[_challengeID].remainder -= (reward * MULTIPLIER);
        if (voting.isPassed(_challengeID)) {
            // if challenger won, transfer tokens to challenger
            token.transfer(challengeMap[_challengeID].challenger, reward);
        } else {
            // if publisher won, give tokens to the domain's deposit
            listingMap[sha3(_domain)].currentDeposit += reward;
        }
    }

// --------
// 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];

        require(token.transfer(listing.owner, listing.currentDeposit));

        delete listingMap[domainHash];
    }
}

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

contract Registry {

    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;        // 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 remainder;         // remainder tokens from flooring rewards to be claimed by the winner
    }

    // maps challengeIDs to associated challenge data
    mapping(uint => Challenge) 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;
    // maps hash of parameter name to parameter value
    mapping(bytes32 => uint) public Parameters;

    // Global Variables
    StandardToken token;
    PLCRVoting voting;

    // Constants
    bytes32 constant private MINDEPOSIT_h = sha3("minDeposit");
    bytes32 constant private MINPARAMDEPOSIT_h = sha3("minParamDeposit");
    bytes32 constant private APPLYSTAGELEN_h = sha3("applyStageLen");
    bytes32 constant private COMMITPERIODLEN_h = sha3("commitPeriodLen");
    bytes32 constant private REVEALPERIODLEN_h = sha3("revealPeriodLen");
    bytes32 constant private DISPENSATIONPCT_h = sha3("dispensationPct");
    bytes32 constant private VOTEQUORUM_h = sha3("voteQuorum");
    uint256 constant private MULTIPLIER = 10 ** 18;  // used to help represent doubles as ints in token rewards

// ------------
// CONSTRUCTOR:
// ------------
    /// @param _minDeposit      minimum deposit for listing to be whitelisted
    /// @param _minParamDeposit minimum deposit to propose a parameter change
    /// @param _applyStage      length of period in which applicants wait to be whitelisted
    /// @param _dispensationPct percentage of losing party's deposit distributed to winning party
    /// @param _commitPeriodLen length of commit period for voting
    /// @param _revealPeriodLen length of reveal period for voting
    /// @param _voteQuorum      type of majority out of 100 necessary for vote success

    function Registry(
        address _tokenAddr,
        uint _minDeposit,
        uint _minParamDeposit,
        uint _applyStageLen,
        uint _commitPeriodLen,
        uint _revealPeriodLen,
        uint _dispensationPct,
        uint _voteQuorum
    ) {
        token = StandardToken(_tokenAddr);
        voting = new PLCRVoting(_tokenAddr);
        Parameters[MINDEPOSIT_h] = _minDeposit;
        Parameters[MINPARAMDEPOSIT_h] = _minParamDeposit;
        Parameters[APPLYSTAGELEN_h] = _applyStageLen;
        Parameters[DISPENSATIONPCT_h] = _dispensationPct;
        Parameters[COMMITPERIODLEN_h] = _commitPeriodLen;
        Parameters[REVEALPERIODLEN_h] = _revealPeriodLen;
        Parameters[VOTEQUORUM_h] = _voteQuorum;
    }

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

    //Allow a user to start an application
    //take tokens from user and set apply stage end time
    function apply(string domain) external {
        require(!isWhitelisted(domain));
        require(!appExists(domain));

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

        //transfer tokens
        uint minDeposit = Parameters[MINDEPOSIT_h];
        require(token.transferFrom(listing.owner, this, minDeposit));

        //set apply stage end time
        listing.applicationExpiry = block.timestamp + Parameters[APPLYSTAGELEN_h];
        listing.currentDeposit = minDeposit;
    }

    //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;
    }

    //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(token.transfer(msg.sender, amount));

        listing.currentDeposit -= amount;
    }

    //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(isWhitelisted(domain));
        // cannot exit during ongoing challenge
        require(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);
        require(challengeMap[listing.challengeID].resolved); // prevent multiple challenges

        if (listing.currentDeposit < Parameters[MINDEPOSIT_h]) {
            // not enough tokens, publisher auto-delisted
            resetListing(domain);
            return 0;
        }
        //take tokens from challenger
        uint deposit = Parameters[MINDEPOSIT_h];
        require(token.transferFrom(msg.sender, this, deposit));
        //start poll
        uint pollID = voting.startPoll(domain,
            Parameters[VOTEQUORUM_h],
            Parameters[COMMITPERIODLEN_h],
            Parameters[REVEALPERIODLEN_h]
        );

        challengeMap[pollID] = Challenge({
            challenger: msg.sender,
            rewardPool: ((100 - Parameters[DISPENSATIONPCT_h]) * deposit) / 100,
            stake: deposit,
            resolved: false,
            remainder: 0
        });

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

        return pollID;
    }

    // whitelist domain if apply stage ended without a challenge
    function updateStatus(string domain) public {
        bytes32 domainHash = sha3(domain);
        uint challengeID = listingMap[domainHash].challengeID;
        require(!challengeMap[challengeID].resolved);  // require processed flag to be false

        // IF NO CHALLENGE AFTER APPLY STAGE
        if (challengeID == 0 && isExpired(listingMap[domainHash].applicationExpiry)) {
            listingMap[domainHash].whitelisted = true;
        } else {
        // PROCESS THE RESULT OF THE POLL

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

            if (voting.isPassed(challengeID)) {
                listingMap[domainHash].whitelisted = true;
                listingMap[domainHash].currentDeposit += stake; // give stake back to applicant
            } else {
                resetListing(domain); // whitelisted = false
                require(token.transfer(challengeMap[challengeID].challenger, stake)); // give stake to challenger
            }

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

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

    // called by voter to claim reward for each completed vote
    function claimReward(uint _challengeID, uint _salt) public {
        // ensure voter has not already claimed tokens
        require(tokenClaims[_challengeID][msg.sender] == false);
        uint reward = calculateTokens(_challengeID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        tokenClaims[_challengeID][msg.sender] = true;
    }

    // helper function to claimReward()
    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function calculateTokens(uint _challengeID, uint _salt, address _voter) private returns(uint) {
        uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_challengeID);
        uint256 voterTokens = voting.getNumPassingTokens(_voter, _challengeID, _salt);

        uint256 rewardPool = challengeMap[_challengeID].rewardPool * MULTIPLIER;
        uint256 numerator = voterTokens * rewardPool;
        uint256 denominator = totalTokens * MULTIPLIER;
        uint256 remainder = numerator % denominator;

        // save remainder tokens in the form of decimal numbers with 18 places represented
        // as a uint256
        challengeMap[_challengeID].remainder += remainder;

        return numerator / denominator;
    }

    // gives reminder tokens from poll to a designated claimer
    // the claimer is the winner of the challenge
    // for every poll there will be ~0.5 nano AdTokens burned,
    // since the winner cannot withdraw a decimal amount of nano AdToken
    function claimExtraReward(uint _challengeID, string _domain) public {
        // uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_challengeID);
        uint256 reward = challengeMap[_challengeID].remainder / (MULTIPLIER);
        // reward = reward / totalTokens; // should this be here?
        challengeMap[_challengeID].remainder -= (reward * MULTIPLIER);
        if (voting.isPassed(_challengeID)) {
            // if challenger won, transfer tokens to challenger
            token.transfer(challengeMap[_challengeID].challenger, reward);
        } else {
            // if publisher won, give tokens to the domain's deposit
            listingMap[sha3(_domain)].currentDeposit += reward;
        }
    }

// --------
// 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];

        require(token.transfer(listing.owner, listing.currentDeposit));

        delete listingMap[domainHash];
    }
}