Update WeiFund_Beta_Launch.sol

Update WeiFund_Beta_Launch.sol

// WeiFund v1.0
// Start, donate, payout and refund crowdfunding campaigns on Ethereum
// If campaign goal is not reached by the stated expiry, all funds are refundable back to oringial contributor accounts
// If campaign goal is reached or surpassed by stated expiry, all raised funds will be paid out to campaign beneficiary

/// @title The core WeiFund confirgutation hook interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFundConfig {
    function newCampaign(uint _campaignID, address _owner, uint _fundingGoal){}
    function contribute(uint _campaignID, address _contributorAddress, uint _amountContributed){}
    function refund(uint _campaignID, address _contributorAddress, uint _amountRefunded){}
    function payout(uint _campaignID, uint _amountRaised){}
}

/// @title The core WeiFund crowdfunding interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFundInterface {
    /// @notice New Campaign; create a new crowdfunding campaign
    /// @dev This method starts a new crowdfunding campaign and fires the onNewCampaign event when transacted.
    /// @param _name The campaign name
    /// @param _beneficiary The address of the beneficiary for this campaign
    /// @param _fundingGoal The funding goal of the campaign. If this goal is not met by the timelimit, all ether will be refunded to the respective contributers
    /// @param _expiry When the campaign will expire and contributions can no longer be made
    /// @param _config The configuration address
    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) {}

    /// @notice Contribute (the campaign ID); contribute ether to a WeiFund campaign
    /// @dev This method will contribute an amount of ether to the campaign at ID _cid. All contribution data will be stored so that the issuance of digital assets can be made out to the contributor address
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign
    /// @param _beneficiary (Contribute As Address) This allows a user to contribute on behalf of another address, if left empty, the from sender address is used as the primary Funder address
    function contribute(uint _campaignID, address _beneficiary) {}

    /// @notice Refund (the campaign ID); refund your contribution of a failed or expired crowdfunding campaign.
    /// @dev This method will refund the amount you contributed to a WeiFund campaign, if that campaign has failed to meet it's funding goal or has expired.
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign to be refunded
    function refund(uint _campaignID) {}

    /// @notice Payout (the campaign ID); this will payout a successfull crowdfunding campaign to the benificiary address
    /// @dev This method will payout a successfull WeiFund crowdfunding campaign to the benificiary address specified. Any person can trigger the payout by calling this method.
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign
    function payout(uint _campaignID) {}

    /// @notice User Campaign ID (the address of the user, the user campaign ID); get the campaign ID of one of the users crowdfunding campaigns.
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _userAddress The address of the campaign operator.
    /// @param _userCampaignID The user campaign ID
    /// @return _campaignID The campaign ID
    function userCampaignID(address _userAddress, uint _userCampaignID) constant returns (uint _campaignID) {}

    /// @notice Total Number of Campaigns Started for a Given User
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _userAddress The user's address
    /// @return _numCampaigns The number of campaigns
    function totalUserCampaigns(address _userAddress) constant returns (uint _numCampaigns) {}

    /// @notice The total number of campaigns on WeiFund
    /// @dev This method returns the total number of campaigns on WeiFund as an unsigned integer
    /// @return _numCampaigns The number of campaigns
    function totalCampaigns() constant returns (uint _numCampaigns) {}

    /// @notice Contributor At ID;
    /// @dev For retrieving the contributor data at a specific contributor ID
    /// @param _campaignID The address of the campaign operator.
    /// @param _contributorID The user campaign ID
    /// @return _contributor, _beneficiary, _amountContributed, _refunded
    function contributorAt(uint _campaignID, uint _contributorID) constant returns (address _contributor,
                                                                                address _beneficiary,
                                                                                uint _amountContributed,
                                                                                bool _refunded){}
    function createdAt(uint _campaignID) public constant returns (uint){}
    function contributorID(uint _campaignID, address _contributorAddress) constant returns (uint) {}
    function ownerOf(uint _campaignID) constant returns (address){}
    function beneficiaryOf(uint _campaignID) constant returns (address){}
    function configOf(uint _campaignID) constant returns (address){}
    function amountRaisedBy(uint _campaignID) constant returns (uint){}
    function fundingGoalOf(uint _campaignID) constant returns (uint){}
    function expiryOf(uint _campaignID) constant returns (uint){}
    function totalContributors(uint _campaignID) constant returns (uint){}

    function isContributor(uint _campaignID, address _contributorAddress) constant returns (bool) {}
    function isOwner(uint _campaignID, address _owner) constant returns (bool){}
    function hasFailed(uint _campaignID) constant returns (bool){}
    function isSuccess(uint _campaignID) constant returns (bool){}
    function isPaidOut(uint _campaignID) constant returns (bool){}
    function totalRefunded(uint _campaignID) constant returns (uint){}
    function isRefunded(uint _campaignID) constant returns (bool){}

    event CampaignCreated(address indexed _owner, uint indexed _campaignID);
    event Contributed(address indexed _contributor, uint indexed _campaignID, uint _amountContributed);
    event PaidOut(address indexed _beneficiary, uint indexed _campaignID, uint _amountPaid);
    event Refunded(address indexed _contributor, uint indexed _campaignID, uint _amountRefunded);
}

/// @title WeiFund - A Decentralized Crowdfunding Platform
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFund is WeiFundInterface {
    // @notice User; A user is an account that has started campaigns on WeiFund
    // @dev This object stores all pertinant campaign operator data, such as how many campaigns the operator has started, and the campaign ID's of all the campaigns they have or are operating
    struct User {
        uint numCampaigns;
        mapping(uint => uint) campaigns;
    }

    // @notice Contributor; This object helps store the pertinant contributer data.
    // @dev This object stores the contributer data, such as the contributer address, and amount.
    struct Contributor {
        address addr;
        address beneficiary;
        uint amountContributed;
        bool refunded;
    }

    // @notice Campaign; The crowdfunding campaign object
    // @dev This object stores all the pertinant campaign data, such as: the name, beneificary, fundingGoal, and the funder data.
    struct Campaign {
        string name;
        address owner;
        address beneficiary;
        address config;
        bool paidOut;
        uint expiry;
        uint fundingGoal;
        uint amountRaised;
        uint created;
        uint numContributors;
        mapping (uint => Contributor) contributors;
        mapping (address => uint) toContributor;
    }

    /// @notice numCampaigns; The total number of crowdfunding campaigns started on WeiFund
    /// @dev This is the uint store that contains the number of the total amount of all crowdfunding campaigns started on WeiFund. This is also used to generate campaign ID numbers.
    uint public numCampaigns;

    /// @notice Campaigns (the campaign ID); Get the campaign data at the specified campaign ID
    /// @dev This data store maps campaign ID's to stored Campaign objects. With this method you can access any crowdfunding campaign started on WeiFund.
    mapping (uint => Campaign) public campaigns;

    /// @notice Users (the user address); Get the number of campaigns a user has started
    /// @dev This will return a user object that contains the number of campaigns a user has started. Use the userCampaigns method to the ID's to the crowdfunding campaigns that they have started.
    mapping (address => User) public users;

    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) public {
        if(_fundingGoal <= 0 || _expiry <= now)
            throw;

        uint _campaignID = numCampaigns++; // campaignID is return variable
        Campaign c = campaigns[_campaignID];  // assigns reference
        c.name = _name;
        c.owner = msg.sender;
        c.beneficiary = _beneficiary;
        c.fundingGoal = _fundingGoal;
        c.expiry = _expiry;
        c.created = now;
        c.config = _config;

        User u = users[msg.sender];
        uint u_campaignID = u.numCampaigns++;
        u.campaigns[u_campaignID] = _campaignID;

        CampaignCreated(msg.sender, _campaignID);

        if(c.config != address(0))
            WeiFundConfig(c.config).newCampaign(_campaignID, msg.sender, _fundingGoal);
    }

    function contribute(uint _campaignID, address _beneficiary) public {
        Campaign c = campaigns[_campaignID];

        if(now > c.expiry || msg.value == 0)
            throw;

        uint backerID = c.numContributors++;
        Contributor backer = c.contributors[backerID];
        backer.addr = msg.sender;
        backer.beneficiary = _beneficiary;
        backer.amountContributed = msg.value;
        c.amountRaised += backer.amountContributed;
        c.toContributor[msg.sender] = backerID;
        Contributed(msg.sender, _campaignID, c.amountRaised);

        if(c.config != address(0))
            WeiFundConfig(c.config).contribute(_campaignID, msg.sender, msg.value);
    }

    function refund(uint _campaignID) public {
        Campaign c = campaigns[_campaignID];

        if (!hasFailed(_campaignID))
            throw;

        Contributor backer = c.contributors[c.toContributor[msg.sender]];

        if(backer.amountContributed <= 0 || backer.refunded)
            throw;

        address receiver = backer.addr;

        if(backer.addr != address(0))
            receiver = backer.addr;

        receiver.send(backer.amountContributed);
        Refunded(receiver, _campaignID, backer.amountContributed);
        backer.refunded = true;

        if(c.config != address(0))
            WeiFundConfig(c.config).refund(_campaignID, receiver, backer.amountContributed);
    }

    function payout(uint _campaignID) public {
        Campaign c = campaigns[_campaignID];

        if(!isSuccess(_campaignID))
            throw;

        c.beneficiary.send(c.amountRaised);
        PaidOut(msg.sender, _campaignID, c.amountRaised);
        c.amountRaised = 0;
        c.paidOut = true;

        if(c.config != address(0))
            WeiFundConfig(c.config).payout(_campaignID, c.amountRaised);
    }

    function userCampaignID(address _userAddress, uint _userCampaignID) public constant returns (uint _campaignID) {
        User u = users[_userAddress];

        return u.campaigns[_userCampaignID];
    }

    function totalUserCampaigns(address _userAddress) constant returns (uint _numCampaigns) {
        User u = users[_userAddress];

        return u.numCampaigns;
    }

    function totalCampaigns() constant returns (uint _numCampaigns) {
        return numCampaigns;
    }

    function contributorAt(uint _campaignID, uint _contributorID) public constant returns (address _contributor,
                                                                                address _beneficiary,
                                                                                uint _amountContributed,
                                                                                bool _refunded) {
        Campaign c = campaigns[_campaignID];

        return (c.contributors[_contributorID].addr,
                c.contributors[_contributorID].beneficiary,
                c.contributors[_contributorID].amountContributed,
                c.contributors[_contributorID].refunded);
    }

    function contributorID(uint _campaignID, address _contributorAddress) public constant returns (uint) {
        Campaign c = campaigns[_campaignID];

        return c.toContributor[_contributorAddress];
    }

    function isContributor(uint _campaignID, address _contributorAddress) public constant returns (bool) {
        Campaign c = campaigns[_campaignID];

        if(c.contributors[c.toContributor[_contributorAddress]].amountContributed != 0)
            return true;
    }

    function ownerOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.owner;
    }

    function beneficiaryOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.beneficiary;
    }

    function configOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.config;
    }

    function amountRaisedBy(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.amountRaised;
    }

    function fundingGoalOf(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.fundingGoal;
    }

    function expiryOf(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.expiry;
    }

    function createdAt(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.created;
    }

    function totalContributors(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.numContributors;
    }

    function isOwner(uint _campaignID, address _owner) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if(c.owner == _owner)
            return true;
    }

    function hasFailed(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if (now > c.expiry
            && c.amountRaised < c.fundingGoal
            && c.amountRaised > 0)
            return true;
    }

    function isSuccess(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if (c.amountRaised >= c.fundingGoal)
            return true;
    }

    function isPaidOut(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        return c.paidOut;
    }

    function totalRefunded(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];
        uint refunded = 0;

        if(!hasFailed(_campaignID))
            return 0;

        for(uint contributorID = 0; contributorID < c.numContributors; contributorID++) {
            if(c.contributors[contributorID].refunded == true)
                refunded += c.contributors[contributorID].amountContributed;
        }

        return refunded;
    }

    function isRefunded(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        for(uint contributorID = 0; contributorID < c.numContributors; contributorID++) {
            if(c.contributors[contributorID].refunded != true)
                return false;
        }

        return true;
    }
}

// WeiFund v1.0
// Start, donate, payout and refund crowdfunding campaigns on Ethereum
// If campaign goal is not reached by the stated expiry, all funds are refundable back to oringial contributor accounts
// If campaign goal is reached or surpassed by stated expiry, all raised funds will be paid out to campaign beneficiary

/// @title The core WeiFund confirgutation hook interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFundConfig {
    function CampaignCreated(uint _campaignID, address _owner, uint _fundingGoal){}
    function Contributed(uint _campaignID, address _contributorAddress, uint _amountContributed){}
    function Refunded(uint _campaignID, address _contributorAddress, uint _amountRefunded){}
    function PaidOut(uint _campaignID, uint _amountRaised){}
}

/// @title The core WeiFund crowdfunding interface
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFundInterface {
    /// @notice New Campaign; create a new crowdfunding campaign
    /// @dev This method starts a new crowdfunding campaign and fires the onNewCampaign event when transacted.
    /// @param _name The campaign name
    /// @param _beneficiary The address of the beneficiary for this campaign
    /// @param _fundingGoal The funding goal of the campaign. If this goal is not met by the timelimit, all ether will be refunded to the respective contributers
    /// @param _expiry When the campaign will expire and contributions can no longer be made
    /// @param _config The configuration address
    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) {}

    /// @notice Contribute (the campaign ID); contribute ether to a WeiFund campaign
    /// @dev This method will contribute an amount of ether to the campaign at ID _cid. All contribution data will be stored so that the issuance of digital assets can be made out to the contributor address
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign
    /// @param _beneficiary (Contribute As Address) This allows a user to contribute on behalf of another address, if left empty, the from sender address is used as the primary Funder address
    function contribute(uint _campaignID, address _beneficiary) {}

    /// @notice Refund (the campaign ID); refund your contribution of a failed or expired crowdfunding campaign.
    /// @dev This method will refund the amount you contributed to a WeiFund campaign, if that campaign has failed to meet it's funding goal or has expired.
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign to be refunded
    function refund(uint _campaignID) {}

    /// @notice Payout (the campaign ID); this will payout a successfull crowdfunding campaign to the benificiary address
    /// @dev This method will payout a successfull WeiFund crowdfunding campaign to the benificiary address specified. Any person can trigger the payout by calling this method.
    /// @param _campaignID (Campaign ID) The ID number of the crowdfunding campaign
    function payout(uint _campaignID) {}

    /// @notice User Campaign ID (the address of the user, the user campaign ID); get the campaign ID of one of the users crowdfunding campaigns.
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _userAddress The address of the campaign operator.
    /// @param _uCampaignID The user campaign ID
    /// @return _campaignID The campaign ID
    function userCampaignID(address _userAddress, uint _uCampaignID) constant returns (uint _campaignID) {}

    /// @notice Total Number of Campaigns Started for a Given User
    /// @dev This method will get the campaign ID of one of the users crowdfunding campaigns, by looking up the campaign with a user campaign ID. All campaign owners and their campaigns are stored with WeiFund.
    /// @param _userAddress The address of the campaign operator.
    /// @return _numCampaigns The number of campaigns
    function totalCampaigns(address _userAddress) constant returns (uint _numCampaigns) {}

    /// @notice Contributor At ID;
    /// @dev For retrieving the contributor data at a specific contributor ID
    /// @param _campaignID The address of the campaign operator.
    /// @param _contributorID The user campaign ID
    /// @return _contributor, _beneficiary, _amountContributed, _refunded
    function contributorAt(uint _campaignID, uint _contributorID) constant returns (address _contributor,
                                                                                address _beneficiary,
                                                                                uint _amountContributed,
                                                                                bool _refunded){}
    function createdAt(uint _campaignID) public constant returns (uint){}
    function contributorID(uint _campaignID, address _contributorAddress) constant returns (uint) {}
    function ownerOf(uint _campaignID) constant returns (address){}
    function beneficiaryOf(uint _campaignID) constant returns (address){}
    function configOf(uint _campaignID) constant returns (address){}
    function amountRaisedBy(uint _campaignID) constant returns (uint){}
    function fundingGoalOf(uint _campaignID) constant returns (uint){}
    function expiryOf(uint _campaignID) constant returns (uint){}
    function totalContributors(uint _campaignID) constant returns (uint){}

    function isContributor(uint _campaignID, address _contributorAddress) constant returns (bool) {}
    function isOwner(uint _campaignID, address _owner) constant returns (bool){}
    function hasFailed(uint _campaignID) constant returns (bool){}
    function isSuccess(uint _campaignID) constant returns (bool){}
    function isPaidOut(uint _campaignID) constant returns (bool){}
    function totalRefunded(uint _campaignID) constant returns (uint){}
    function isRefunded(uint _campaignID) constant returns (bool){}

    event CampaignCreated(address indexed _sender, uint indexed _campaignID);
    event Contributed(address indexed _sender, uint indexed _campaignID, uint _amountContributed);
    event PaidOut(address indexed _sender, uint indexed _campaignID, uint _amountPayedout);
    event Refunded(address indexed _sender, uint indexed _campaignID, uint _amountRefunded);
}

/// @title WeiFund - A Decentralized Crowdfunding Platform
/// @author Nick Dodson <thenickdodson@gmail.com>
contract WeiFund is WeiFundInterface {
    // @notice User; This object stores the campaign operator data
    // @dev This object stores all pertinant campaign operator data, such as how many campaigns the operator has started, and the campaign ID's of all the campaigns they have or are operating
    struct User {
        uint numCampaigns;
        mapping(uint => uint) campaigns;
    }

    // @notice Contributor; This object helps store the pertinant contributer data.
    // @dev This object stores the contributer data, such as the contributer address, and amount.
    struct Contributor {
        address addr;
        address beneficiary;
        uint amountContributed;
        bool refunded;
    }

    // @notice Campaign; The crowdfunding campaign object
    // @dev This object stores all the pertinant campaign data, such as: the name, beneificary, fundingGoal, and the funder data.
    struct Campaign {
        string name;
        address owner;
        address beneficiary;
        address config;
        bool paidOut;
        uint expiry;
        uint fundingGoal;
        uint amountRaised;
        uint created;
        uint numContributors;
        mapping (uint => Contributor) contributors;
        mapping (address => uint) toContributor;
    }

    /// @notice numCampaigns; The total number of crowdfunding campaigns started on WeiFund
    /// @dev This is the uint store that contains the number of the total amount of all crowdfunding campaigns started on WeiFund. This is also used to generate campaign ID numbers.
    uint public numCampaigns;

    /// @notice Campaigns (the campaign ID); Get the campaign data at the specified campaign ID
    /// @dev This data store maps campaign ID's to stored Campaign objects. With this method you can access any crowdfunding campaign started on WeiFund.
    mapping (uint => Campaign) public campaigns;

    /// @notice Users (the user address); Get the number of campaigns a user has started
    /// @dev This will return a user object that contains the number of campaigns a user has started. Use the userCampaigns method to the ID's to the crowdfunding campaigns that they have started.
    mapping (address => User) public users;

    function newCampaign(string _name, address _beneficiary, uint _fundingGoal, uint _expiry, address _config) public {
        if(_fundingGoal <= 0 || _expiry <= now)
            throw;

        uint _campaignID = numCampaigns++; // campaignID is return variable
        Campaign c = campaigns[_campaignID];  // assigns reference
        c.name = _name;
        c.owner = msg.sender;
        c.beneficiary = _beneficiary;
        c.fundingGoal = _fundingGoal;
        c.expiry = _expiry;
        c.created = now;
        c.config = _config;

        User u = users[msg.sender];
        uint u_campaignID = u.numCampaigns++;
        u.campaigns[u_campaignID] = _campaignID;

        CampaignCreated(msg.sender, _campaignID);

        if(c.config != address(0))
            WeiFundConfig(c.config).CampaignCreated(_campaignID, msg.sender, _fundingGoal);
    }

    function contribute(uint _campaignID, address _beneficiary) public {
        Campaign c = campaigns[_campaignID];

        if(now > c.expiry || msg.value == 0)
            throw;

        uint backerID = c.numContributors++;
        Contributor backer = c.contributors[backerID];
        backer.addr = msg.sender;
        backer.beneficiary = _beneficiary;
        backer.amountContributed = msg.value;
        c.amountRaised += backer.amountContributed;
        c.toContributor[msg.sender] = backerID;
        Contributed(msg.sender, _campaignID, c.amountRaised);

        if(c.config != address(0))
            WeiFundConfig(c.config).Contributed(_campaignID, msg.sender, msg.value);
    }

    function refund(uint _campaignID) public {
        Campaign c = campaigns[_campaignID];

        if (!hasFailed(_campaignID))
            throw;

        Contributor backer = c.contributors[c.toContributor[msg.sender]];

        if(backer.amountContributed <= 0 || backer.refunded)
            throw;

        address receiver = backer.addr;

        if(backer.addr != address(0))
            receiver = backer.addr;

        receiver.send(backer.amountContributed);
        Refunded(receiver, _campaignID, backer.amountContributed);
        backer.refunded = true;

        if(c.config != address(0))
            WeiFundConfig(c.config).Refunded(_campaignID, receiver, backer.amountContributed);
    }

    function payout(uint _campaignID) public {
        Campaign c = campaigns[_campaignID];

        if(!isSuccess(_campaignID))
            throw;

        c.beneficiary.send(c.amountRaised);
        PaidOut(msg.sender, _campaignID, c.amountRaised);
        c.amountRaised = 0;
        c.paidOut = true;

        if(c.config != address(0))
            WeiFundConfig(c.config).PaidOut(_campaignID, c.amountRaised);
    }

    function userCampaignID(address _userAddress, uint _uCampaignID) public constant returns (uint _campaignID) {
        User u = users[_userAddress];
        _campaignID = u.campaigns[_uCampaignID];
    }

    function totalCampaigns(address _userAddress) constant returns (uint _numCampaigns) {
        User u = users[_userAddress];
        _numCampaigns = u.numCampaigns;
    }

    function contributorAt(uint _campaignID, uint _contributorID) public constant returns (address _contributor,
                                                                                address _beneficiary,
                                                                                uint _amountContributed,
                                                                                bool _refunded) {
        Campaign c = campaigns[_campaignID];

        return (c.contributors[_contributorID].addr,
                c.contributors[_contributorID].beneficiary,
                c.contributors[_contributorID].amountContributed,
                c.contributors[_contributorID].refunded);
    }

    function contributorID(uint _campaignID, address _contributorAddress) public constant returns (uint) {
        Campaign c = campaigns[_campaignID];

        return c.toContributor[_contributorAddress];
    }

    function isContributor(uint _campaignID, address _contributorAddress) public constant returns (bool) {
        Campaign c = campaigns[_campaignID];

        if(c.contributors[c.toContributor[_contributorAddress]].amountContributed != 0)
            return true;
    }

    function ownerOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.owner;
    }

    function beneficiaryOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.beneficiary;
    }

    function configOf(uint _campaignID) public constant returns (address){
        Campaign c = campaigns[_campaignID];

        return c.config;
    }

    function amountRaisedBy(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.amountRaised;
    }

    function fundingGoalOf(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.fundingGoal;
    }

    function expiryOf(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.expiry;
    }

    function createdAt(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.created;
    }

    function totalContributors(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];

        return c.numContributors;
    }

    function isOwner(uint _campaignID, address _owner) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if(c.owner == _owner)
            return true;
    }

    function hasFailed(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if (now > c.expiry
            && c.amountRaised < c.fundingGoal
            && c.amountRaised > 0)
            return true;
    }

    function isSuccess(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        if (c.amountRaised >= c.fundingGoal)
            return true;
    }

    function isPaidOut(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        return c.paidOut;
    }

    function totalRefunded(uint _campaignID) public constant returns (uint){
        Campaign c = campaigns[_campaignID];
        uint refunded = 0;

        if(!hasFailed(_campaignID))
            return 0;

        for(uint contributorID = 0; contributorID < c.numContributors; contributorID++) {
            if(c.contributors[contributorID].refunded == true)
                refunded += c.contributors[contributorID].amountContributed;
        }

        return refunded;
    }

    function isRefunded(uint _campaignID) public constant returns (bool){
        Campaign c = campaigns[_campaignID];

        for(uint contributorID = 0; contributorID < c.numContributors; contributorID++) {
            if(c.contributors[contributorID].refunded != true)
                return false;
        }

        return true;
    }
}


tweak TokenTimelock documentation

tweak TokenTimelock documentation

pragma solidity ^0.4.11;

import './ERC20Basic.sol';

/**
 * @title TokenTimelock
 * @dev TokenTimelock is a token holder contract that will allow a
 * beneficiary to extract the tokens after a given release time
 */
contract TokenTimelock {

  // ERC20 basic token contract being held
  ERC20Basic token;

  // beneficiary of tokens after they are released
  address beneficiary;

  // timestamp when token release is enabled
  uint releaseTime;

  function TokenTimelock(ERC20Basic _token, address _beneficiary, uint _releaseTime) {
    require(_releaseTime > now);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }

  /**
   * @dev beneficiary claims tokens held by time lock
   */
  function claim() {
    require(msg.sender == beneficiary);
    require(now >= releaseTime);

    uint amount = token.balanceOf(this);
    require(amount > 0);

    token.transfer(beneficiary, amount);
  }
}

pragma solidity ^0.4.11;

import './ERC20Basic.sol';

/**
 * @title TokenTimelock
 * @dev TokenTimelock is a token holder contract that will allow a
 * beneficiary to extract the tokens after a time has passed
 */
contract TokenTimelock {

  // ERC20 basic token contract being held
  ERC20Basic token;

  // beneficiary of tokens after they are released
  address beneficiary;

  // timestamp where token release is enabled
  uint releaseTime;

  function TokenTimelock(ERC20Basic _token, address _beneficiary, uint _releaseTime) {
    require(_releaseTime > now);
    token = _token;
    beneficiary = _beneficiary;
    releaseTime = _releaseTime;
  }

  /**
   * @dev beneficiary claims tokens held by time lock
   */
  function claim() {
    require(msg.sender == beneficiary);
    require(now >= releaseTime);

    uint amount = token.balanceOf(this);
    require(amount > 0);

    token.transfer(beneficiary, amount);
  }
}


Rename token to Enjin Coin

Rename token to Enjin Coin

pragma solidity ^0.4.15;
import './ERC20Token.sol';
import './Owned.sol';

contract ENJToken is ERC20Token, Owned {

///////////////////////////////////////// VARIABLE INITIALIZATION /////////////////////////////////////////

    uint256 public totalSupply = 1 * (10**9) * 10**18;

    //  Constants
    uint256 public maxPresaleSupply = 600 * 10**6 * 10**18;           // Total presale supply at max bonus
    uint256 public minCrowdsaleAllocation = 200 * 10**6 * 10**18;     // Min amount for crowdsale
    uint256 public incentivisationAllocation = 100 * 10**6 * 10**18;  // Incentivisation Allocation
    uint256 public advisorsAllocation = 50 * 10**6 * 10**18;          // Advisors Allocation
    uint256 public enjinTeamAllocation = 50 * 10**6 * 10**18;         // Enjin Team allocation

    address public crowdFundAddress;                                  // Address of the crowdfund
    address public advisorAddress;                                    // Enjin advisor's address
    address public incentivisationFundAddress;                        // Address that holds the incentivization funds
    address public enjinTeamAddress;                                  // Enjin Team address

    //  Variables
    uint256 public totalAllocatedToAdvisors = 0;                      // Counter to keep track of total Advisor allocation
    uint256 public totalAllocatedToTeam = 0;                          // Counter to keep track of team token allocation
    uint256 public totalAllocated = 0;                                // Counter to keep track of team token allocation
    uint256 public startTime = 1507032000;                            // 10/03/2017 @ 12:00pm (UTC) crowdsale start time (in seconds)
    uint256 public endTime = 1509494340;                              // 10/31/2017 @ 11:59pm (UTC) crowdsale end time (in seconds)

    bool internal isReleasedToPublic = false;                         // Flag to allow transfer/transferFrom before the end of the crowdfund

    bool internal releaseFirstAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSecondAllocationTranche = false;             // Flags to keep track of Team allocation tranches
    bool internal releaseThirdAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseFourthAllocationTranche = false;             // Flags to keep track of Team allocation tranches
    bool internal releaseFifthAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSixthAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSeventhAllocationTranche = false;            // Flags to keep track of Team allocation tranches

///////////////////////////////////////// MODIFIERS /////////////////////////////////////////

    // Enjin Team timelock
    modifier safeTimelock() {
        require(now >= endTime + 6 * 4 weeks);
        _;
    }

    // Advisor Team timelock
    modifier advisorTimelock() {
        require(now >= endTime + 2 * 4 weeks);
        _;
    }

    // Function only accessible by the Crowdfund contract
    modifier crowdfundOnly() {
        require(msg.sender == crowdFundAddress);
        _;
    }

    ///////////////////////////////////////// CONSTRUCTOR /////////////////////////////////////////

    /**
        @dev constructor
        @param _crowdFundAddress   Crowdfund address
        @param _advisorAddress     Advisor address
    */
    function ENJToken(address _crowdFundAddress, address _advisorAddress, address _incentivisationFundAddress, address _enjinTeamAddress)
    ERC20Token("Enjin Coin", "ENJ", 18)
     {
        crowdFundAddress = _crowdFundAddress;
        advisorAddress = _advisorAddress;
        enjinTeamAddress = _enjinTeamAddress;
        incentivisationFundAddress = _incentivisationFundAddress;
        balanceOf[_crowdFundAddress] = minCrowdsaleAllocation + maxPresaleSupply; // Total presale + crowdfund tokens
        balanceOf[_incentivisationFundAddress] = incentivisationAllocation;       // 10% Allocated for Marketing and Incentivisation
        totalAllocated += incentivisationAllocation;                              // Add to total Allocated funds
    }

///////////////////////////////////////// ERC20 OVERRIDE /////////////////////////////////////////

    /**
        @dev send coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transfer(address _to, uint256 _value) greaterThanZero(_value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transfer(_to, _value));
            return true;
        }
        revert();
    }

    /**
        @dev an account/contract attempts to get the coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _from    source address
        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transferFrom(address _from, address _to, uint256 _value) greaterThanZero(_value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transferFrom(_from, _to, _value));
            return true;
        }
        revert();
    }

///////////////////////////////////////// ALLOCATION FUNCTIONS /////////////////////////////////////////

    /**
        @dev release Enjin Team Token allocation
        throws if before timelock (6 months) ends and if no initiated by the owner of the contract
        returns true if valid
        Schedule goes as follows:
        6 months: 25%
        9 months: 12.5%
        12 months: 12.5%
        15 months: 12.5%
        18 months: 12.5%
        21 months: 12.5%
        24 months: 12.5%
        @return true if successful, throws if not
    */
    function releaseEnjinTeamTokens() ownerOnly returns(bool success) {
        require(totalAllocatedToTeam < enjinTeamAllocation);
        uint256 enjinTeamAlloc = enjinTeamAllocation / 1000;

        if (now > endTime + 6 * 4 weeks && !releaseFirstAllocationTranche) {
            releaseFirstAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 250));
            return true;
        } else  if (now > endTime + 9 * 4 weeks && !releaseSecondAllocationTranche) {
            releaseSecondAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 12 * 4 weeks && !releaseThirdAllocationTranche) {
            releaseThirdAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 15 * 4 weeks && !releaseFourthAllocationTranche) {
            releaseFourthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 18 * 4 weeks && !releaseFifthAllocationTranche) {
            releaseFifthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 21 * 4 weeks && !releaseSixthAllocationTranche) {
            releaseSixthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 24 * 4 weeks && !releaseSeventhAllocationTranche) {
            releaseSeventhAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        }
        revert();
    }

    /**
        @dev transfers Tokens from Enjin allocation to Team
        throws if before first timelock (6 months) ends and if no initiated by the owner of the contract
        @return true if successful, throws if not
    */
    function transferTeamAllocation(uint256 _amount) ownerOnly internal {
        balanceOf[enjinTeamAddress] = safeAdd(balanceOf[enjinTeamAddress], _amount);
        Transfer(this, enjinTeamAddress, _amount);
        totalAllocated = safeAdd(totalAllocated, _amount);
        totalAllocatedToTeam = safeAdd(totalAllocatedToTeam, _amount);
    }

    /**
        @dev release Advisors Token allocation
        throws if before timelock (2 months) ends or if no initiated by the advisors address
        or if there is no more allocation to give out
        returns true if valid

        @return true if successful, throws if not
    */
    function releaseAdvisorTokens() advisorTimelock ownerOnly returns(bool success) {
        require(totalAllocatedToAdvisors == 0);
        balanceOf[advisorAddress] = safeAdd(balanceOf[advisorAddress], advisorsAllocation);
        Transfer(this, advisorAddress, advisorsAllocation);
        totalAllocated = safeAdd(totalAllocated, advisorsAllocation);
        totalAllocatedToAdvisors = advisorsAllocation;
        return true;
    }

    /**
        @dev Retrive unsold tokens from the crowdfund
        throws if before timelock (6 months from end of Crowdfund) ends and if no initiated by the owner of the contract
        returns true if valid

        @return true if successful, throws if not
    */
    function retrieveUnsoldTokens() safeTimelock ownerOnly returns(bool success) {
        uint256 amountOfTokens = balanceOf[crowdFundAddress];
        balanceOf[incentivisationFundAddress] = safeAdd(balanceOf[incentivisationFundAddress], amountOfTokens);
        totalAllocated = safeAdd(totalAllocated, amountOfTokens);
        balanceOf[crowdFundAddress] = 0;
        return true;
    }

    /**
        @dev Keep track of token allocations
        can only be called by the crowdfund contract
    */
    function addToAllocation(uint256 _amount) crowdfundOnly {
        totalAllocated = safeAdd(totalAllocated, _amount);
    }

    /**
        @dev Function to allow transfers
        can only be called by the owner of the contract
        Transfers will be allowed regradless after the crowdfund end time.
    */
    function allowTransfers() ownerOnly {
        isReleasedToPublic = true;
    }

    /**
        @dev User transfers are allowed/rejected
        Transfers are forbidden before the end of the crowdfund
    */
    function isTransferAllowed() internal returns(bool) {
        if (now > endTime || isReleasedToPublic == true) {
            return true;
        }
        return false;
    }

    function () {
        revert();
    }
}

pragma solidity ^0.4.15;
import './ERC20Token.sol';
import './Owned.sol';

contract ENJToken is ERC20Token, Owned {

///////////////////////////////////////// VARIABLE INITIALIZATION /////////////////////////////////////////

    uint256 public totalSupply = 1 * (10**9) * 10**18;

    //  Constants
    uint256 public maxPresaleSupply = 600 * 10**6 * 10**18;           // Total presale supply at max bonus
    uint256 public minCrowdsaleAllocation = 200 * 10**6 * 10**18;     // Min amount for crowdsale
    uint256 public incentivisationAllocation = 100 * 10**6 * 10**18;  // Incentivisation Allocation
    uint256 public advisorsAllocation = 50 * 10**6 * 10**18;          // Advisors Allocation
    uint256 public enjinTeamAllocation = 50 * 10**6 * 10**18;         // Enjin Team allocation

    address public crowdFundAddress;                                  // Address of the crowdfund
    address public advisorAddress;                                    // Enjin advisor's address
    address public incentivisationFundAddress;                        // Address that holds the incentivization funds
    address public enjinTeamAddress;                                  // Enjin Team address

    //  Variables
    uint256 public totalAllocatedToAdvisors = 0;                      // Counter to keep track of total Advisor allocation
    uint256 public totalAllocatedToTeam = 0;                          // Counter to keep track of team token allocation
    uint256 public totalAllocated = 0;                                // Counter to keep track of team token allocation
    uint256 public startTime = 1507032000;                            // 10/03/2017 @ 12:00pm (UTC) crowdsale start time (in seconds)
    uint256 public endTime = 1509494340;                              // 10/31/2017 @ 11:59pm (UTC) crowdsale end time (in seconds)

    bool internal isReleasedToPublic = false;                         // Flag to allow transfer/transferFrom before the end of the crowdfund

    bool internal releaseFirstAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSecondAllocationTranche = false;             // Flags to keep track of Team allocation tranches
    bool internal releaseThirdAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseFourthAllocationTranche = false;             // Flags to keep track of Team allocation tranches
    bool internal releaseFifthAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSixthAllocationTranche = false;              // Flags to keep track of Team allocation tranches
    bool internal releaseSeventhAllocationTranche = false;            // Flags to keep track of Team allocation tranches

///////////////////////////////////////// MODIFIERS /////////////////////////////////////////

    // Enjin Team timelock
    modifier safeTimelock() {
        require(now >= endTime + 6 * 4 weeks);
        _;
    }

    // Advisor Team timelock
    modifier advisorTimelock() {
        require(now >= endTime + 2 * 4 weeks);
        _;
    }

    // Function only accessible by the Crowdfund contract
    modifier crowdfundOnly() {
        require(msg.sender == crowdFundAddress);
        _;
    }

    ///////////////////////////////////////// CONSTRUCTOR /////////////////////////////////////////

    /**
        @dev constructor
        @param _crowdFundAddress   Crowdfund address
        @param _advisorAddress     Advisor address
    */
    function ENJToken(address _crowdFundAddress, address _advisorAddress, address _incentivisationFundAddress, address _enjinTeamAddress)
    ERC20Token("ENJ Coin", "ENJ", 18)
     {
        crowdFundAddress = _crowdFundAddress;
        advisorAddress = _advisorAddress;
        enjinTeamAddress = _enjinTeamAddress;
        incentivisationFundAddress = _incentivisationFundAddress;
        balanceOf[_crowdFundAddress] = minCrowdsaleAllocation + maxPresaleSupply; // Total presale + crowdfund tokens
        balanceOf[_incentivisationFundAddress] = incentivisationAllocation;       // 10% Allocated for Marketing and Incentivisation
        totalAllocated += incentivisationAllocation;                              // Add to total Allocated funds
    }

///////////////////////////////////////// ERC20 OVERRIDE /////////////////////////////////////////

    /**
        @dev send coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transfer(address _to, uint256 _value) greaterThanZero(_value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transfer(_to, _value));
            return true;
        }
        revert();
    }

    /**
        @dev an account/contract attempts to get the coins
        throws on any error rather then return a false flag to minimize user errors
        in addition to the standard checks, the function throws if transfers are disabled

        @param _from    source address
        @param _to      target address
        @param _value   transfer amount

        @return true if the transfer was successful, throws if it wasn't
    */
    function transferFrom(address _from, address _to, uint256 _value) greaterThanZero(_value) public returns (bool success) {
        if (isTransferAllowed() == true || msg.sender == crowdFundAddress || msg.sender == incentivisationFundAddress) {
            assert(super.transferFrom(_from, _to, _value));
            return true;
        }
        revert();
    }

///////////////////////////////////////// ALLOCATION FUNCTIONS /////////////////////////////////////////

    /**
        @dev release Enjin Team Token allocation
        throws if before timelock (6 months) ends and if no initiated by the owner of the contract
        returns true if valid
        Schedule goes as follows:
        6 months: 25%
        9 months: 12.5%
        12 months: 12.5%
        15 months: 12.5%
        18 months: 12.5%
        21 months: 12.5%
        24 months: 12.5%
        @return true if successful, throws if not
    */
    function releaseEnjinTeamTokens() ownerOnly returns(bool success) {
        require(totalAllocatedToTeam < enjinTeamAllocation);
        uint256 enjinTeamAlloc = enjinTeamAllocation / 1000;

        if (now > endTime + 6 * 4 weeks && !releaseFirstAllocationTranche) {
            releaseFirstAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 250));
            return true;
        } else  if (now > endTime + 9 * 4 weeks && !releaseSecondAllocationTranche) {
            releaseSecondAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 12 * 4 weeks && !releaseThirdAllocationTranche) {
            releaseThirdAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 15 * 4 weeks && !releaseFourthAllocationTranche) {
            releaseFourthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 18 * 4 weeks && !releaseFifthAllocationTranche) {
            releaseFifthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 21 * 4 weeks && !releaseSixthAllocationTranche) {
            releaseSixthAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        } else  if (now > endTime + 24 * 4 weeks && !releaseSeventhAllocationTranche) {
            releaseSeventhAllocationTranche = true;
            transferTeamAllocation(safeMul(enjinTeamAlloc, 125));
            return true;
        }
        revert();
    }

    /**
        @dev transfers Tokens from Enjin allocation to Team
        throws if before first timelock (6 months) ends and if no initiated by the owner of the contract
        @return true if successful, throws if not
    */
    function transferTeamAllocation(uint256 _amount) ownerOnly internal {
        balanceOf[enjinTeamAddress] = safeAdd(balanceOf[enjinTeamAddress], _amount);
        Transfer(this, enjinTeamAddress, _amount);
        totalAllocated = safeAdd(totalAllocated, _amount);
        totalAllocatedToTeam = safeAdd(totalAllocatedToTeam, _amount);
    }

    /**
        @dev release Advisors Token allocation
        throws if before timelock (2 months) ends or if no initiated by the advisors address
        or if there is no more allocation to give out
        returns true if valid

        @return true if successful, throws if not
    */
    function releaseAdvisorTokens() advisorTimelock ownerOnly returns(bool success) {
        require(totalAllocatedToAdvisors == 0);
        balanceOf[advisorAddress] = safeAdd(balanceOf[advisorAddress], advisorsAllocation);
        Transfer(this, advisorAddress, advisorsAllocation);
        totalAllocated = safeAdd(totalAllocated, advisorsAllocation);
        totalAllocatedToAdvisors = advisorsAllocation;
        return true;
    }

    /**
        @dev Retrive unsold tokens from the crowdfund
        throws if before timelock (6 months from end of Crowdfund) ends and if no initiated by the owner of the contract
        returns true if valid

        @return true if successful, throws if not
    */
    function retrieveUnsoldTokens() safeTimelock ownerOnly returns(bool success) {
        uint256 amountOfTokens = balanceOf[crowdFundAddress];
        balanceOf[incentivisationFundAddress] = safeAdd(balanceOf[incentivisationFundAddress], amountOfTokens);
        totalAllocated = safeAdd(totalAllocated, amountOfTokens);
        balanceOf[crowdFundAddress] = 0;
        return true;
    }

    /**
        @dev Keep track of token allocations
        can only be called by the crowdfund contract
    */
    function addToAllocation(uint256 _amount) crowdfundOnly {
        totalAllocated = safeAdd(totalAllocated, _amount);
    }

    /**
        @dev Function to allow transfers
        can only be called by the owner of the contract
        Transfers will be allowed regradless after the crowdfund end time.
    */
    function allowTransfers() ownerOnly {
        isReleasedToPublic = true;
    }

    /**
        @dev User transfers are allowed/rejected
        Transfers are forbidden before the end of the crowdfund
    */
    function isTransferAllowed() internal returns(bool) {
        if (now > endTime || isReleasedToPublic == true) {
            return true;
        }
        return false;
    }

    function () {
        revert();
    }
}


contracts/KetherSortition.sol: wip

contracts/KetherSortition.sol: wip

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

import "@openzeppelin/contracts/access/Ownable.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);
}

library Errors {
  string constant OnlyMagistrate = "only active magistrate can do this";
}

contract KetherSortition is Ownable {
  // MagistrateId is the tokenId of KetherNFT that controls the royalties purse for this epoch.
  uint256 public magistrateId;

  uint256 constant EPOCH_DURATION = 30 days; // TODO: Confirm OpenSea's royalty interval
  uint256 epochStarted;

  IERC721 ketherNFTContract;
  IKetherHomepage ketherContract;

  address[] nominations;
  bytes32[] precommits;

  constructor(address _ketherNFTContract, address _ketherContract) {
    ketherNFTContract = IERC721(_ketherNFTContract);
    ketherContract = IKetherHomepage(_ketherContract);

    epochStart = block.timestamp;
  }

  // Internal helpers:

  function _resetElection() internal {
    nominations = address[]();
    precommits = bytes32[]();
  }

  // Views:

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

  function pixelCount(address owner) public view returns (uint256) {
    // XXX: Implement this
  }

  function chooseNextMagistrate() public view returns (uint256) {
    // TODO: Make sure duplicate nominations don't do anything weird
  }

  // External interface:

  /**
   * @dev Nominate address of held ads as a candidate for magistrate in the next epoch.
   * @param hashedSeed Keccak256 hash of entropy seed precommitment.
   */
  function nominateSelf(bytes32 hashedSeed) external {
    // XXX: Implement this
  }

  /*
    Precommit notes:
    1. (N x On-chain txn) Each participant submits hash(seed), where seed is a random value
    2. Epoch expires (preventing further submissions)
    3. (N x On-chain txn) Each participant reveals seed (smart contract confirms it matches original hash(seed))
    4. Seeds are mixed together (xor)
    5. (On-chain txn) Final mixed seed value used for entropy

    Chainlink VRF option:
    1. (N x On-chain txn) Each participant nominates themselves (no precommit needed)
    2. Epoch expires
    3. (On-chain txn) Election is called which sends a Chainlink.requestRandomness (with LINK payment)
    4. (On-chain txn) Chainlink does a callback to KetherSortition.fulfillRandomness with a random value, which we save
    5. (On-chain txn) Use saved random value to elect.
  */

  // Only magistrate:

  function withdraw() public {
    require(_msgSender() == _getAdOwner(magistrateId), Errors.OnlyMagistrate);
    // TODO: ...
  }

  // Only owner (admin helpers):

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

error: pathspec 'contracts/KetherSortition.sol' did not match any file(s) known to git


Remove inheritance that conflicts with upgradeable transpiler

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

contract BadBeaconNoImpl {
}

contract BadBeaconNotContract {
    function implementation() external view returns (address) {
        return address(0x1);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "../proxy/IBeacon.sol";

contract BadBeaconNoImpl {
}

contract BadBeaconNotContract is IBeacon {
    function implementation() external view override returns (address) {
        return address(0x1);
    }
}


Use require in Migrations and update for 0.7.0

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.8.0;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    require(
      msg.sender == owner,
      "This function is restricted to the contract's owner"
    );
    _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.21 <0.7.0;

contract Migrations {
  address public owner;
  uint public last_completed_migration;

  constructor() public {
    owner = msg.sender;
  }

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }
}


strings cannot be indexed, apparently

strings cannot be indexed, apparently

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

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

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

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

        return this.balance - totalRefunds;
    }

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

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

        owner.transfer(uint(toWithdraw));
    }

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

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

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

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

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

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

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

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

        pot.isOpen = false;

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

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

            LogPotExpired(name);
            return;
        }

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

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

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

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

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

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

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

pragma solidity ^0.4.11;
contract PotOfEther {

    struct Pot {
        string name;
        uint buyIn;

        address[] players;

        uint lastPlayerBlockNumber;

        bool isOpen;
    }

    address public owner;

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

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

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

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

        return this.balance - totalRefunds;
    }

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

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

        owner.transfer(uint(toWithdraw));
    }

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

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

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

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

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

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

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

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

        pot.isOpen = false;

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

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

            LogPotExpired(name);
            return;
        }

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

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

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

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

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

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

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


Science takes a transaction fee on purchases; fix warnings

pragma solidity ^0.4.15;

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 = "039fdcdcfc31968c6938863ac1d293854ba810bbfa0bcd72b1f4cc2d544f3d08";

    address owner;

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

    uint public nextStarIndexToAssign = 0; // TODO: unused, remove?

    bool public allStarsAssigned = false;
    bool public canClaimStars = 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, uint256 transactionFee);
    event StarNoLongerForSale(uint indexed starIndex);
    event ClaimFeeUpdated(uint256 indexed newClaimFee);
    event TransactionFeePercentageUpdated(uint8 indexed newTransactionFeePercentage);

/* 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 = 119615;                               // 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
        claimFee = 15000000000000000;                       // Price of claiming a star
        transactionFeePercentage = 5;                       // Whole number percentage (0-100)
    }

    function setInitialOwner(address to, uint starIndex) onlyOwner {
        if (allStarsAssigned) revert();
        if (starIndex >= totalSupply) revert();
        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;
        canClaimStars = true;
    }

    function updateCSV(string newHash, uint256 newTotalSupply) onlyOwner {
        if (newTotalSupply < totalSupply) revert();         // We should only be able to add stars to the db
        csvHash = newHash;
        if (totalSupply != newTotalSupply) {
            starsRemainingToAssign = newTotalSupply - totalSupply;
            canClaimStars = true;
            totalSupply = newTotalSupply;
        }
    }

    function updateClaimFee(uint256 newClaimFee) onlyOwner {
        claimFee = newClaimFee;
        ClaimFeeUpdated(newClaimFee);
    }

    function updateTransactionFeePercentage(uint8 newTransactionFeePercentage) onlyOwner {
        if (newTransactionFeePercentage > 5) revert();                  // Prevent the fee from ever being more than 5%
        if (newTransactionFeePercentage < 0) revert();
        transactionFeePercentage = newTransactionFeePercentage;
        TransactionFeePercentageUpdated(newTransactionFeePercentage);
    }

    function getStar(uint starIndex) payable {
        if (!allStarsAssigned && !canClaimStars) revert();
        if (starsRemainingToAssign == 0) revert();
        if (starIndexToAddress[starIndex] != 0x0) revert();
        if (starIndex >= totalSupply) revert();
        if (msg.value < claimFee) revert();
        pendingWithdrawals[owner] += msg.value;
        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[msg.sender]++;
        starsRemainingToAssign--;
        if (starsRemainingToAssign == 0) {
            canClaimStars = false;
        }
        Assign(msg.sender, starIndex);
    }

// Transfer ownership of a star to another user without requiring payment
    function transferStar(address to, uint starIndex) {
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        if (starIndex >= totalSupply) revert();
        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 storage 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) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        if (starIndex >= totalSupply) revert();
        starsOfferedForSale[starIndex] = Offer(false, starIndex, msg.sender, 0, 0x0);
        StarNoLongerForSale(starIndex);
    }

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

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

    function buyStar(uint starIndex) payable {
        if (!allStarsAssigned) revert();
        Offer storage offer = starsOfferedForSale[starIndex];
        if (starIndex >= totalSupply) revert();
        if (!offer.isForSale) revert();                // star not actually for sale
        if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) revert();  // star not supposed to be sold to this user
        if (msg.value < offer.minValue) revert();      // Didn't send enough ETH
        if (offer.seller != starIndexToAddress[starIndex]) revert(); // 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);
        uint256 transactionFee = msg.value * (transactionFeePercentage / 100);
        uint256 toSeller = msg.value - transactionFee;
        pendingWithdrawals[owner] += transactionFee;
        pendingWithdrawals[seller] += toSeller;
        StarBought(starIndex, msg.value, seller, msg.sender, transactionFee);

    // Check for the case where there is a bid from the new owner and refund it.
    // Any other bid can stay in place.
        Bid storage 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) revert();
        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 >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] == 0x0) revert();
        if (starIndexToAddress[starIndex] == msg.sender) revert();
        if (msg.value == 0) revert();
        Bid storage existing = starBids[starIndex];
        if (msg.value <= existing.value) revert();
        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 >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        address seller = msg.sender;
        Bid storage bid = starBids[starIndex];
        if (bid.value == 0) revert();
        if (bid.value < minPrice) revert();

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

        starsOfferedForSale[starIndex] = Offer(false, starIndex, bid.bidder, 0, 0x0);
        uint256 transactionFee = bid.value * (transactionFeePercentage / 100);
        uint256 toSeller = bid.value - transactionFee;
        starBids[starIndex] = Bid(false, starIndex, 0x0, 0);
        pendingWithdrawals[owner] += transactionFee;
        pendingWithdrawals[seller] += toSeller;
        StarBought(starIndex, bid.value, seller, bid.bidder, transactionFee);
    }

    function withdrawBidForStar(uint starIndex) {
        if (starIndex >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] == 0x0) revert();
        if (starIndexToAddress[starIndex] == msg.sender) revert();
        Bid storage bid = starBids[starIndex];
        if (bid.bidder != msg.sender) revert();
        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.15;

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 = "039fdcdcfc31968c6938863ac1d293854ba810bbfa0bcd72b1f4cc2d544f3d08";

    address owner;

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

    uint public nextStarIndexToAssign = 0; // TODO: unused, remove?

    bool public allStarsAssigned = false;
    bool public canClaimStars = 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);
    event ClaimFeeUpdated(uint256 indexed claimFee);

/* 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 = 119615;                               // 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
        claimFee = 15000000000000000;                       // Price of claiming a star
    }

    function setInitialOwner(address to, uint starIndex) onlyOwner {
        if (allStarsAssigned) revert();
        if (starIndex >= totalSupply) revert();
        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;
        canClaimStars = true;
    }

    function updateCSV(string newHash, uint256 newTotalSupply) onlyOwner {
        if (newTotalSupply < totalSupply) revert();         // We should only be able to add stars to the db
        csvHash = newHash;
        if (totalSupply != newTotalSupply) {
            starsRemainingToAssign = newTotalSupply - totalSupply;
            canClaimStars = true;
            totalSupply = newTotalSupply;
        }
    }

    function updateClaimFee(uint256 newClaimFee) onlyOwner {
        claimFee = newClaimFee;
        ClaimFeeUpdated(newClaimFee);
    }

    function getStar(uint starIndex) {
        if (!allStarsAssigned && !canClaimStars) revert();
        if (starsRemainingToAssign == 0) revert();
        if (starIndexToAddress[starIndex] != 0x0) revert();
        if (starIndex >= totalSupply) revert();
        if (msg.value < claimFee) revert();
        pendingWithdrawals[owner] += msg.value;
        starIndexToAddress[starIndex] = msg.sender;
        balanceOf[msg.sender]++;
        starsRemainingToAssign--;
        if (starsRemainingToAssign == 0) {
            canClaimStars = false;
        }
        Assign(msg.sender, starIndex);
    }

// Transfer ownership of a star to another user without requiring payment
    function transferStar(address to, uint starIndex) {
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        if (starIndex >= totalSupply) revert();
        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 storage 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) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        if (starIndex >= totalSupply) revert();
        starsOfferedForSale[starIndex] = Offer(false, starIndex, msg.sender, 0, 0x0);
        StarNoLongerForSale(starIndex);
    }

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

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

    function buyStar(uint starIndex) payable {
        if (!allStarsAssigned) revert();
        Offer storage offer = starsOfferedForSale[starIndex];
        if (starIndex >= totalSupply) revert();
        if (!offer.isForSale) revert();                // star not actually for sale
        if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) revert();  // star not supposed to be sold to this user
        if (msg.value < offer.minValue) revert();      // Didn't send enough ETH
        if (offer.seller != starIndexToAddress[starIndex]) revert(); // 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 storage 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) revert();
        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 >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] == 0x0) revert();
        if (starIndexToAddress[starIndex] == msg.sender) revert();
        if (msg.value == 0) revert();
        Bid storage existing = starBids[starIndex];
        if (msg.value <= existing.value) revert();
        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 >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] != msg.sender) revert();
        address seller = msg.sender;
        Bid storage bid = starBids[starIndex];
        if (bid.value == 0) revert();
        if (bid.value < minPrice) revert();

        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 >= totalSupply) revert();
        if (!allStarsAssigned) revert();
        if (starIndexToAddress[starIndex] == 0x0) revert();
        if (starIndexToAddress[starIndex] == msg.sender) revert();
        Bid storage bid = starBids[starIndex];
        if (bid.bidder != msg.sender) revert();
        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);
    }

}