StandardBounties/contracts/TokenBounty.sol

202 lines
6.4 KiB
Solidity

pragma solidity ^0.4.11;
import "./StandardBounty.sol";
import "./inherited/HumanStandardToken.sol";
/// @title TokenBounty
/// @dev Used to pay out individuals or groups in ERC20 tokens for task
/// fulfillment through stepwise work submission, acceptance, and payment
/// @dev extension of StandardBounty to pay out bounties with a given ERC20 token
/// @author Mark Beylin <mark.beylin@consensys.net>
contract TokenBounty is StandardBounty {
/*
* Storage
*/
StandardToken public tokenContract;
/*
* Modifiers
*/
modifier amountEqualsValue(uint value) {
if (value != 0){
require(tokenContract.transferFrom(msg.sender, this, value));
}
require(tokenContract.balanceOf(this) >= value);
_;
}
modifier validateFunding() {
uint total = 0;
for (uint i = 0 ; i < fulfillmentAmounts.length; i++){
total = total + fulfillmentAmounts[i];
}
require (tokenContract.balanceOf(this) >= (total + amountToPay));
_;
}
modifier amountToPayRemains(uint _milestoneId) {
require((amountToPay + fulfillmentAmounts[_milestoneId]) <= tokenContract.balanceOf(this));
_;
}
modifier unpaidAmountRemains(uint _milestoneId) {
if (numAccepted[_milestoneId] != 0){
require((unpaidAmount + fulfillmentAmounts[_milestoneId]) <= tokenContract.balanceOf(this));
}
_;
}
modifier fundsRemainToPayUnpaidAmounts(uint _difference, uint _milestoneId){
if (numAccepted[_milestoneId] == 0){
require(tokenContract.balanceOf(this) >= (unpaidAmount + _difference));
}
_;
}
modifier fundsRemainForAmountToPay(uint _difference, uint _milestoneId){
require(tokenContract.balanceOf(this) >= (amountToPay + (_difference * (numAccepted[_milestoneId] - numPaid[_milestoneId]))));
_;
}
/*
* Public functions
*/
/// @dev TokenBounty(): instantiates a new draft token bounty
/// @param _issuer the address of the intended issuer of the bounty
/// @param _deadline the unix timestamp after which fulfillments will no longer be accepted
/// @param _data the requirements of the bounty
/// @param _fulfillmentAmounts the amount of wei to be paid out for each successful fulfillment
/// @param _totalFulfillmentAmounts the sum of the individual fulfillment amounts
/// @param _arbiter the address of the arbiter who can mediate claims
/// @param _tokenAddress the address of the token contract
function TokenBounty(
address _issuer,
uint _deadline,
string _data,
uint[] _fulfillmentAmounts,
uint _totalFulfillmentAmounts,
address _arbiter,
address _tokenAddress
)
StandardBounty(
_issuer,
_deadline,
_data,
_fulfillmentAmounts,
_totalFulfillmentAmounts,
_arbiter
)
{
tokenContract = StandardToken(_tokenAddress);
}
/// @dev contribute(): a function allowing anyone to contribute ether to a
/// bounty, as long as it is still before its deadline. Shouldn't keep
/// ether by accident (hence 'value').
/// @param value the amount being contributed in ether to prevent accidental deposits
/// @notice Please note you funds will be at the mercy of the issuer
/// and can be drained at any moment. Be careful!
function contribute (uint value)
public
payable
isBeforeDeadline
isNotDead
amountIsNotZero(value)
amountEqualsValue(value)
{
require(msg.value == 0);
ContributionAdded(msg.sender, value);
}
/// @notice Send funds to activate the bug bounty
/// @dev activateBounty(): activate a bounty so it may pay out
/// @param value the amount being contributed in ether to prevent
/// accidental deposits
function activateBounty(uint value)
public
payable
isBeforeDeadline
onlyIssuer
amountEqualsValue(value)
validateFunding
{
require(msg.value == 0);
transitionToState(BountyStages.Active);
ContributionAdded(msg.sender, msg.value);
BountyActivated(msg.sender);
}
/// @dev fulfillmentPayment(): accept a given fulfillment, and send
/// the fulfiller their owed funds
/// @param _milestoneId the id of the milestone being paid
/// @param _fulfillmentId the index of the fulfillment being accepted
function fulfillmentPayment(uint _milestoneId, uint _fulfillmentId)
public
validateMilestoneIndex(_milestoneId)
validateFulfillmentArrayIndex(_milestoneId, _fulfillmentId)
onlyFulfiller(_milestoneId, _fulfillmentId)
checkFulfillmentIsApprovedAndUnpaid(_milestoneId, _fulfillmentId)
{
fulfillments[_milestoneId][_fulfillmentId].paid = true;
numPaid[_milestoneId]++;
amountToPay -= fulfillmentAmounts[_milestoneId];
tokenContract.transfer(fulfillments[_milestoneId][_fulfillmentId].fulfiller, fulfillmentAmounts[_milestoneId]);
FulfillmentPaid(msg.sender, _milestoneId, _fulfillmentId);
}
/// @dev killBounty(): drains the contract of it's remaining
/// funds, and moves the bounty into stage 3 (dead) since it was
/// either killed in draft stage, or never accepted any fulfillments
function killBounty()
public
onlyIssuer
{
tokenContract.transfer(issuer, tokenContract.balanceOf(this) - amountToPay);
transitionToState(BountyStages.Dead);
BountyKilled();
}
/// @dev changeBounty(): allows the issuer to change all bounty storage
/// members simultaneously
/// @param _newIssuer the new address of the issuer
/// @param _newDeadline the new deadline for the bounty
/// @param _newData the new requirements of the bounty
/// @param _newFulfillmentAmounts the new fulfillment amounts
/// @param _totalFulfillmentAmounts the sum of the new fulfillment amounts
/// @param _tokenAddress the address of the token contract
function changeBounty(address _newIssuer,
uint _newDeadline,
string _newData,
uint[] _newFulfillmentAmounts,
uint _totalFulfillmentAmounts,
address _newArbiter,
address _tokenAddress)
public
onlyIssuer
validateDeadline(_newDeadline)
amountsNotZeroAndEqualSum(_newFulfillmentAmounts, _totalFulfillmentAmounts)
isAtStage(BountyStages.Draft)
{
issuer = _newIssuer;
deadline = _newDeadline;
data = _newData;
fulfillmentAmounts = _newFulfillmentAmounts;
arbiter = _newArbiter;
unpaidAmount = _totalFulfillmentAmounts;
tokenContract = HumanStandardToken(_tokenAddress);
}
}