pragma solidity 0.5.12;
pragma experimental ABIEncoderV2;
contract Token {
/* This is a slight change to the ERC20 base standard.
function totalSupply() pure returns (uint256 supply);
is replaced with:
uint256 public totalSupply;
This automatically creates a getter function for the totalSupply.
This is moved to the base contract since public getter functions are not
currently recognised as an implementation of the matching abstract
function by the compiler.
*/
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) view public returns (uint256 balance);
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success);
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
event Transfer(address _from, address _to, uint256 _value);
event Approval(address _owner, address _spender, uint256 _value);
}
contract ERC20Token is Token {
function transfer(address _to, uint256 _value) public returns (bool success) {
//Default assumes totalSupply can't be over max (2^256 - 1).
//If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap.
//Replace the if with this one instead.
//if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[msg.sender] >= _value && _value > 0) {
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
} else { return false; }
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
//same as above. Replace this line with the following if you want to protect against wrapping uints.
//if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
} else { return false; }
}
function balanceOf(address _owner) view public returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) view public returns (uint256 remaining) {
return allowed[_owner][_spender];
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
}
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}
/**
* Utility library of inline functions on addresses
*/
library AddressUtils {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param addr address to check
* @return whether the target address is a contract
*/
function isContract(address addr) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solium-disable-next-line security/no-inline-assembly
assembly { size := extcodesize(addr) }
return size > 0;
}
}
/**
* @title ERC165
* @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md
*/
interface ERC165 {
/**
* @notice Query if a contract implements an interface
* @param _interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 _interfaceId)
external
view
returns (bool);
}
/**
* @title SupportsInterfaceWithLookup
* @author Matt Condon (@shrugs)
* @dev Implements ERC165 using a lookup table.
*/
contract SupportsInterfaceWithLookup is ERC165 {
bytes4 public constant InterfaceId_ERC165 = 0x01ffc9a7;
/**
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) internal supportedInterfaces;
/**
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself
*/
constructor()
public
{
_registerInterface(InterfaceId_ERC165);
}
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 _interfaceId)
external
view
returns (bool)
{
return supportedInterfaces[_interfaceId];
}
/**
* @dev private method for registering an interface
*/
function _registerInterface(bytes4 _interfaceId)
internal
{
require(_interfaceId != 0xffffffff);
supportedInterfaces[_interfaceId] = true;
}
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract ERC721Receiver {
/**
* @dev Magic value to be returned upon successful reception of an NFT
* Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`,
* which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
*/
bytes4 internal constant ERC721_RECEIVED = 0xf0b9e5ba;
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safetransfer`. This function MAY throw to revert and reject the
* transfer. This function MUST use 50,000 gas or less. Return of other
* than the magic value MUST result in the transaction being reverted.
* Note: the contract address is always the message sender.
* @param _from The sending address
* @param _tokenId The NFT identifier which is being transfered
* @param _data Additional data with no specified format
* @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
*/
function onERC721Received(
address _from,
uint256 _tokenId,
bytes memory _data
)
public
returns(bytes4);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721Basic is ERC165 {
event Transfer(
address _from,
address _to,
uint256 _tokenId
);
event Approval(
address _owner,
address _approved,
uint256 _tokenId
);
event ApprovalForAll(
address _owner,
address _operator,
bool _approved
);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function exists(uint256 _tokenId) public view returns (bool _exists);
function approve(address _to, uint256 _tokenId) public;
function getApproved(uint256 _tokenId)
public view returns (address _operator);
function setApprovalForAll(address _operator, bool _approved) public;
function isApprovedForAll(address _owner, address _operator)
public view returns (bool);
function transferFrom(address _from, address _to, uint256 _tokenId) public;
function safeTransferFrom(address _from, address _to, uint256 _tokenId)
public;
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
)
public;
}
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
*/
contract ERC721BasicToken is SupportsInterfaceWithLookup, ERC721Basic {
bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
bytes4 private constant InterfaceId_ERC721Exists = 0x4f558e79;
/*
* 0x4f558e79 ===
* bytes4(keccak256('exists(uint256)'))
*/
using SafeMath for uint256;
using AddressUtils for address;
// Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
// which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
bytes4 private constant ERC721_RECEIVED = 0xf0b9e5ba;
// Mapping from token ID to owner
mapping (uint256 => address) internal tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) internal tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => uint256) internal ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) internal operatorApprovals;
uint public testint;
/**
* @dev Guarantees msg.sender is owner of the given token
* @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
*/
modifier onlyOwnerOf(uint256 _tokenId) {
require(ownerOf(_tokenId) == msg.sender);
_;
}
/**
* @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
* @param _tokenId uint256 ID of the token to validate
*/
modifier canTransfer(uint256 _tokenId) {
require(isApprovedOrOwner(msg.sender, _tokenId));
_;
}
constructor()
public
{
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(InterfaceId_ERC721);
_registerInterface(InterfaceId_ERC721Exists);
}
/**
* @dev Gets the balance of the specified address
* @param _owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address _owner) public view returns (uint256) {
require(_owner != address(0));
return ownedTokensCount[_owner];
}
/**
* @dev Gets the owner of the specified token ID
* @param _tokenId uint256 ID of the token to query the owner of
* @return owner address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 _tokenId) public view returns (address) {
address owner = tokenOwner[_tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Returns whether the specified token exists
* @param _tokenId uint256 ID of the token to query the existence of
* @return whether the token exists
*/
function exists(uint256 _tokenId) public view returns (bool) {
address owner = tokenOwner[_tokenId];
return owner != address(0);
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param _to address to be approved for the given token ID
* @param _tokenId uint256 ID of the token to be approved
*/
function approve(address _to, uint256 _tokenId) public {
address owner = ownerOf(_tokenId);
require(_to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
tokenApprovals[_tokenId] = _to;
emit Approval(owner, _to, _tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* @param _tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 _tokenId) public view returns (address) {
return tokenApprovals[_tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param _to operator address to set the approval
* @param _approved representing the status of the approval to be set
*/
function setApprovalForAll(address _to, bool _approved) public {
require(_to != msg.sender);
operatorApprovals[msg.sender][_to] = _approved;
emit ApprovalForAll(msg.sender, _to, _approved);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param _owner owner address which you want to query the approval of
* @param _operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(
address _owner,
address _operator
)
public
view
returns (bool)
{
return operatorApprovals[_owner][_operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible
* Requires the msg sender to be the owner, approved, or operator
* @param _from current owner of the token
* @param _to address to receive the ownership of the given token ID
* @param _tokenId uint256 ID of the token to be transferred
*/
function transferFrom(
address _from,
address _to,
uint256 _tokenId
)
public
canTransfer(_tokenId)
{
require(_from != address(0));
require(_to != address(0));
clearApproval(_from, _tokenId);
removeTokenFrom(_from, _tokenId);
addTokenTo(_to, _tokenId);
emit Transfer(_from, _to, _tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
*
* Requires the msg sender to be the owner, approved, or operator
* @param _from current owner of the token
* @param _to address to receive the ownership of the given token ID
* @param _tokenId uint256 ID of the token to be transferred
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId
)
public
canTransfer(_tokenId)
{
// solium-disable-next-line arg-overflow
safeTransferFrom(_from, _to, _tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg sender to be the owner, approved, or operator
* @param _from current owner of the token
* @param _to address to receive the ownership of the given token ID
* @param _tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
)
public
canTransfer(_tokenId)
{
transferFrom(_from, _to, _tokenId);
// solium-disable-next-line arg-overflow
require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
}
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param _spender address of the spender to query
* @param _tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function isApprovedOrOwner(
address _spender,
uint256 _tokenId
)
internal
view
returns (bool)
{
address owner = ownerOf(_tokenId);
// Disable solium check because of
// https://github.com/duaraghav8/Solium/issues/175
// solium-disable-next-line operator-whitespace
return (
_spender == owner ||
getApproved(_tokenId) == _spender ||
isApprovedForAll(owner, _spender)
);
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param _to The address that will own the minted token
* @param _tokenId uint256 ID of the token to be minted by the msg.sender
*/
function _mint(address _to, uint256 _tokenId) internal {
require(_to != address(0));
addTokenTo(_to, _tokenId);
emit Transfer(address(0), _to, _tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* @param _tokenId uint256 ID of the token being burned by the msg.sender
*/
function _burn(address _owner, uint256 _tokenId) internal {
clearApproval(_owner, _tokenId);
removeTokenFrom(_owner, _tokenId);
emit Transfer(_owner, address(0), _tokenId);
}
/**
* @dev Internal function to clear current approval of a given token ID
* Reverts if the given address is not indeed the owner of the token
* @param _owner owner of the token
* @param _tokenId uint256 ID of the token to be transferred
*/
function clearApproval(address _owner, uint256 _tokenId) internal {
require(ownerOf(_tokenId) == _owner);
if (tokenApprovals[_tokenId] != address(0)) {
tokenApprovals[_tokenId] = address(0);
}
}
/**
* @dev Internal function to add a token ID to the list of a given address
* @param _to address representing the new owner of the given token ID
* @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function addTokenTo(address _to, uint256 _tokenId) internal {
require(tokenOwner[_tokenId] == address(0));
tokenOwner[_tokenId] = _to;
ownedTokensCount[_to] = ownedTokensCount[_to].add(1);
}
/**
* @dev Internal function to remove a token ID from the list of a given address
* @param _from address representing the previous owner of the given token ID
* @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function removeTokenFrom(address _from, uint256 _tokenId) internal {
require(ownerOf(_tokenId) == _from);
ownedTokensCount[_from] = ownedTokensCount[_from].sub(1);
tokenOwner[_tokenId] = address(0);
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param _from address representing the previous owner of the given token ID
* @param _to target address that will receive the tokens
* @param _tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return whether the call correctly returned the expected magic value
*/
function checkAndCallSafeTransfer(
address _from,
address _to,
uint256 _tokenId,
bytes memory _data
)
internal
returns (bool)
{
if (!_to.isContract()) {
return true;
}
bytes4 retval = ERC721Receiver(_to).onERC721Received(
_from, _tokenId, _data);
return (retval == ERC721_RECEIVED);
}
}
contract ERC721BasicTokenMock is ERC721BasicToken {
function mint(address _to, uint256 _tokenId) public {
super._mint(_to, _tokenId);
}
function burn(uint256 _tokenId) public {
super._burn(ownerOf(_tokenId), _tokenId);
}
}
contract StandardBounties {
using SafeMath for uint256;
/*
* Structs
*/
struct Bounty {
address payable[] issuers; // An array of individuals who have complete control over the bounty, and can edit any of its parameters
address[] approvers; // An array of individuals who are allowed to accept the fulfillments for a particular bounty
uint deadline; // The Unix timestamp before which all submissions must be made, and after which refunds may be processed
address token; // The address of the token associated with the bounty (should be disregarded if the tokenVersion is 0)
uint tokenVersion; // The version of the token being used for the bounty (0 for ETH, 20 for ERC20, 721 for ERC721)
uint balance; // The number of tokens which the bounty is able to pay out or refund
bool hasPaidOut; // A boolean storing whether or not the bounty has paid out at least once, meaning refunds are no longer allowed
Fulfillment[] fulfillments; // An array of Fulfillments which store the various submissions which have been made to the bounty
Contribution[] contributions; // An array of Contributions which store the contributions which have been made to the bounty
}
struct Fulfillment {
address payable[] fulfillers; // An array of addresses who should receive payouts for a given submission
address submitter; // The address of the individual who submitted the fulfillment, who is able to update the submission as needed
}
struct Contribution {
address payable contributor; // The address of the individual who contributed
uint amount; // The amount of tokens the user contributed
bool refunded; // A boolean storing whether or not the contribution has been refunded yet
}
/*
* Storage
*/
uint public numBounties; // An integer storing the total number of bounties in the contract
mapping(uint => Bounty) public bounties; // A mapping of bountyIDs to bounties
mapping (uint => mapping (uint => bool)) public tokenBalances; // A mapping of bountyIds to tokenIds to booleans, storing whether a given bounty has a given ERC721 token in its balance
address public owner; // The address of the individual who's allowed to set the metaTxRelayer address
address public metaTxRelayer; // The address of the meta transaction relayer whose _sender is automatically trusted for all contract calls
bool public callStarted; // Ensures mutex for the entire contract
/*
* Modifiers
*/
modifier callNotStarted(){
require(!callStarted);
callStarted = true;
_;
callStarted = false;
}
modifier validateBountyArrayIndex(
uint _index)
{
require(_index < numBounties);
_;
}
modifier validateContributionArrayIndex(
uint _bountyId,
uint _index)
{
require(_index < bounties[_bountyId].contributions.length);
_;
}
modifier validateFulfillmentArrayIndex(
uint _bountyId,
uint _index)
{
require(_index < bounties[_bountyId].fulfillments.length);
_;
}
modifier validateIssuerArrayIndex(
uint _bountyId,
uint _index)
{
require(_index < bounties[_bountyId].issuers.length);
_;
}
modifier validateApproverArrayIndex(
uint _bountyId,
uint _index)
{
require(_index < bounties[_bountyId].approvers.length);
_;
}
modifier onlyIssuer(
address _sender,
uint _bountyId,
uint _issuerId)
{
require(_sender == bounties[_bountyId].issuers[_issuerId]);
_;
}
modifier onlySubmitter(
address _sender,
uint _bountyId,
uint _fulfillmentId)
{
require(_sender ==
bounties[_bountyId].fulfillments[_fulfillmentId].submitter);
_;
}
modifier onlyContributor(
address _sender,
uint _bountyId,
uint _contributionId)
{
require(_sender ==
bounties[_bountyId].contributions[_contributionId].contributor);
_;
}
modifier isApprover(
address _sender,
uint _bountyId,
uint _approverId)
{
require(_sender == bounties[_bountyId].approvers[_approverId]);
_;
}
modifier hasNotPaid(
uint _bountyId)
{
require(!bounties[_bountyId].hasPaidOut);
_;
}
modifier hasNotRefunded(
uint _bountyId,
uint _contributionId)
{
require(!bounties[_bountyId].contributions[_contributionId].refunded);
_;
}
modifier senderIsValid(
address _sender)
{
require(msg.sender == _sender || msg.sender == metaTxRelayer);
_;
}
/*
* Public functions
*/
constructor() public {
// The owner of the contract is automatically designated to be the deployer of the contract
owner = msg.sender;
}
/// @dev setMetaTxRelayer(): Sets the address of the meta transaction relayer
/// @param _relayer the address of the relayer
function setMetaTxRelayer(address _relayer)
external
{
require(msg.sender == owner); // Checks that only the owner can call
require(metaTxRelayer == address(0)); // Ensures the meta tx relayer can only be set once
metaTxRelayer = _relayer;
}
/// @dev issueBounty(): creates a new bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _issuers the array of addresses who will be the issuers of the bounty
/// @param _approvers the array of addresses who will be the approvers of the bounty
/// @param _data the IPFS hash representing the JSON object storing the details of the bounty (see docs for schema details)
/// @param _deadline the timestamp which will become the deadline of the bounty
/// @param _token the address of the token which will be used for the bounty
/// @param _tokenVersion the version of the token being used for the bounty (0 for ETH, 20 for ERC20, 721 for ERC721)
function issueBounty(
address payable _sender,
address payable[] memory _issuers,
address[] memory _approvers,
string memory _data,
uint _deadline,
address _token,
uint _tokenVersion)
public
senderIsValid(_sender)
returns (uint)
{
require(_tokenVersion == 0 || _tokenVersion == 20 || _tokenVersion == 721); // Ensures a bounty can only be issued with a valid token version
require(_issuers.length > 0 || _approvers.length > 0); // Ensures there's at least 1 issuer or approver, so funds don't get stuck
uint bountyId = numBounties; // The next bounty's index will always equal the number of existing bounties
Bounty storage newBounty = bounties[bountyId];
newBounty.issuers = _issuers;
newBounty.approvers = _approvers;
newBounty.deadline = _deadline;
newBounty.tokenVersion = _tokenVersion;
if (_tokenVersion != 0){
newBounty.token = _token;
}
numBounties = numBounties.add(1); // Increments the number of bounties, since a new one has just been added
emit BountyIssued(bountyId,
_sender,
_issuers,
_approvers,
_data, // Instead of storing the string on-chain, it is emitted within the event for easy off-chain consumption
_deadline,
_token,
_tokenVersion);
return (bountyId);
}
/// @param _depositAmount the amount of tokens being deposited to the bounty, which will create a new contribution to the bounty
function issueAndContribute(
address payable _sender,
address payable[] memory _issuers,
address[] memory _approvers,
string memory _data,
uint _deadline,
address _token,
uint _tokenVersion,
uint _depositAmount)
public
payable
returns(uint)
{
uint bountyId = issueBounty(_sender, _issuers, _approvers, _data, _deadline, _token, _tokenVersion);
contribute(_sender, bountyId, _depositAmount);
return (bountyId);
}
/// @dev contribute(): Allows users to contribute tokens to a given bounty.
/// Contributing merits no privelages to administer the
/// funds in the bounty or accept submissions. Contributions
/// are refundable but only on the condition that the deadline
/// has elapsed, and the bounty has not yet paid out any funds.
/// All funds deposited in a bounty are at the mercy of a
/// bounty's issuers and approvers, so please be careful!
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _amount the amount of tokens being contributed
function contribute(
address payable _sender,
uint _bountyId,
uint _amount)
public
payable
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
callNotStarted
{
require(_amount > 0); // Contributions of 0 tokens or token ID 0 should fail
bounties[_bountyId].contributions.push(
Contribution(_sender, _amount, false)); // Adds the contribution to the bounty
if (bounties[_bountyId].tokenVersion == 0){
bounties[_bountyId].balance = bounties[_bountyId].balance.add(_amount); // Increments the balance of the bounty
require(msg.value == _amount);
} else if (bounties[_bountyId].tokenVersion == 20){
bounties[_bountyId].balance = bounties[_bountyId].balance.add(_amount); // Increments the balance of the bounty
require(msg.value == 0); // Ensures users don't accidentally send ETH alongside a token contribution, locking up funds
require(ERC20Token(bounties[_bountyId].token).transferFrom(_sender,
address(this),
_amount));
} else if (bounties[_bountyId].tokenVersion == 721){
tokenBalances[_bountyId][_amount] = true; // Adds the 721 token to the balance of the bounty
require(msg.value == 0); // Ensures users don't accidentally send ETH alongside a token contribution, locking up funds
ERC721BasicToken(bounties[_bountyId].token).transferFrom(_sender,
address(this),
_amount);
} else {
revert();
}
emit ContributionAdded(_bountyId,
bounties[_bountyId].contributions.length - 1, // The new contributionId
_sender,
_amount);
}
/// @dev refundContribution(): Allows users to refund the contributions they've
/// made to a particular bounty, but only if the bounty
/// has not yet paid out, and the deadline has elapsed.
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _contributionId the index of the contribution being refunded
function refundContribution(
address _sender,
uint _bountyId,
uint _contributionId)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateContributionArrayIndex(_bountyId, _contributionId)
onlyContributor(_sender, _bountyId, _contributionId)
hasNotPaid(_bountyId)
hasNotRefunded(_bountyId, _contributionId)
callNotStarted
{
require(now > bounties[_bountyId].deadline); // Refunds may only be processed after the deadline has elapsed
Contribution storage contribution = bounties[_bountyId].contributions[_contributionId];
contribution.refunded = true;
transferTokens(_bountyId, contribution.contributor, contribution.amount); // Performs the disbursal of tokens to the contributor
emit ContributionRefunded(_bountyId, _contributionId);
}
/// @dev refundMyContributions(): Allows users to refund their contributions in bulk
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _contributionIds the array of indexes of the contributions being refunded
function refundMyContributions(
address _sender,
uint _bountyId,
uint[] memory _contributionIds)
public
senderIsValid(_sender)
{
for (uint i = 0; i < _contributionIds.length; i++){
refundContribution(_sender, _bountyId, _contributionIds[i]);
}
}
/// @dev refundContributions(): Allows users to refund their contributions in bulk
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is making the call
/// @param _contributionIds the array of indexes of the contributions being refunded
function refundContributions(
address _sender,
uint _bountyId,
uint _issuerId,
uint[] memory _contributionIds)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
onlyIssuer(_sender, _bountyId, _issuerId)
callNotStarted
{
for (uint i = 0; i < _contributionIds.length; i++){
require(_contributionIds[i] < bounties[_bountyId].contributions.length);
Contribution storage contribution = bounties[_bountyId].contributions[_contributionIds[i]];
require(!contribution.refunded);
contribution.refunded = true;
transferTokens(_bountyId, contribution.contributor, contribution.amount); // Performs the disbursal of tokens to the contributor
}
emit ContributionsRefunded(_bountyId, _sender, _contributionIds);
}
/// @dev drainBounty(): Allows an issuer to drain the funds from the bounty
/// @notice when using this function, if an issuer doesn't drain the entire balance, some users may be able to refund their contributions, while others may not (which is unfair to them). Please use it wisely, only when necessary
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is making the call
/// @param _amounts an array of amounts of tokens to be sent. The length of the array should be 1 if the bounty is in ETH or ERC20 tokens. If it's an ERC721 bounty, the array should be the list of tokenIDs.
function drainBounty(
address payable _sender,
uint _bountyId,
uint _issuerId,
uint[] memory _amounts)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
onlyIssuer(_sender, _bountyId, _issuerId)
callNotStarted
{
if (bounties[_bountyId].tokenVersion == 0 || bounties[_bountyId].tokenVersion == 20){
require(_amounts.length == 1); // ensures there's only 1 amount of tokens to be returned
require(_amounts[0] <= bounties[_bountyId].balance); // ensures an issuer doesn't try to drain the bounty of more tokens than their balance permits
transferTokens(_bountyId, _sender, _amounts[0]); // Performs the draining of tokens to the issuer
} else {
for (uint i = 0; i < _amounts.length; i++){
require(tokenBalances[_bountyId][_amounts[i]]);// ensures an issuer doesn't try to drain the bounty of a token it doesn't have in its balance
transferTokens(_bountyId, _sender, _amounts[i]);
}
}
emit BountyDrained(_bountyId, _sender, _amounts);
}
/// @dev performAction(): Allows users to perform any generalized action
/// associated with a particular bounty, such as applying for it
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _data the IPFS hash corresponding to a JSON object which contains the details of the action being performed (see docs for schema details)
function performAction(
address _sender,
uint _bountyId,
string memory _data)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
{
emit ActionPerformed(_bountyId, _sender, _data); // The _data string is emitted in an event for easy off-chain consumption
}
/// @dev fulfillBounty(): Allows users to fulfill the bounty to get paid out
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _fulfillers the array of addresses which will receive payouts for the submission
/// @param _data the IPFS hash corresponding to a JSON object which contains the details of the submission (see docs for schema details)
function fulfillBounty(
address _sender,
uint _bountyId,
address payable[] memory _fulfillers,
string memory _data)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
{
require(now < bounties[_bountyId].deadline); // Submissions are only allowed to be made before the deadline
require(_fulfillers.length > 0); // Submissions with no fulfillers would mean no one gets paid out
bounties[_bountyId].fulfillments.push(Fulfillment(_fulfillers, _sender));
emit BountyFulfilled(_bountyId,
(bounties[_bountyId].fulfillments.length - 1),
_fulfillers,
_data, // The _data string is emitted in an event for easy off-chain consumption
_sender);
}
/// @dev updateFulfillment(): Allows the submitter of a fulfillment to update their submission
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _fulfillmentId the index of the fulfillment
/// @param _fulfillers the new array of addresses which will receive payouts for the submission
/// @param _data the new IPFS hash corresponding to a JSON object which contains the details of the submission (see docs for schema details)
function updateFulfillment(
address _sender,
uint _bountyId,
uint _fulfillmentId,
address payable[] memory _fulfillers,
string memory _data)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateFulfillmentArrayIndex(_bountyId, _fulfillmentId)
onlySubmitter(_sender, _bountyId, _fulfillmentId) // Only the original submitter of a fulfillment may update their submission
{
bounties[_bountyId].fulfillments[_fulfillmentId].fulfillers = _fulfillers;
emit FulfillmentUpdated(_bountyId,
_fulfillmentId,
_fulfillers,
_data); // The _data string is emitted in an event for easy off-chain consumption
}
/// @dev acceptFulfillment(): Allows any of the approvers to accept a given submission
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _fulfillmentId the index of the fulfillment to be accepted
/// @param _approverId the index of the approver which is making the call
/// @param _tokenAmounts the array of token amounts which will be paid to the
/// fulfillers, whose length should equal the length of the
/// _fulfillers array of the submission. If the bounty pays
/// in ERC721 tokens, then these should be the token IDs
/// being sent to each of the individual fulfillers
function acceptFulfillment(
address _sender,
uint _bountyId,
uint _fulfillmentId,
uint _approverId,
uint[] memory _tokenAmounts)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateFulfillmentArrayIndex(_bountyId, _fulfillmentId)
isApprover(_sender, _bountyId, _approverId)
callNotStarted
{
// now that the bounty has paid out at least once, refunds are no longer possible
bounties[_bountyId].hasPaidOut = true;
Fulfillment storage fulfillment = bounties[_bountyId].fulfillments[_fulfillmentId];
require(_tokenAmounts.length == fulfillment.fulfillers.length); // Each fulfiller should get paid some amount of tokens (this can be 0)
for (uint256 i = 0; i < fulfillment.fulfillers.length; i++){
if (_tokenAmounts[i] > 0){
// for each fulfiller associated with the submission
transferTokens(_bountyId, fulfillment.fulfillers[i], _tokenAmounts[i]);
}
}
emit FulfillmentAccepted(_bountyId,
_fulfillmentId,
_sender,
_tokenAmounts);
}
/// @dev fulfillAndAccept(): Allows any of the approvers to fulfill and accept a submission simultaneously
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _fulfillers the array of addresses which will receive payouts for the submission
/// @param _data the IPFS hash corresponding to a JSON object which contains the details of the submission (see docs for schema details)
/// @param _approverId the index of the approver which is making the call
/// @param _tokenAmounts the array of token amounts which will be paid to the
/// fulfillers, whose length should equal the length of the
/// _fulfillers array of the submission. If the bounty pays
/// in ERC721 tokens, then these should be the token IDs
/// being sent to each of the individual fulfillers
function fulfillAndAccept(
address _sender,
uint _bountyId,
address payable[] memory _fulfillers,
string memory _data,
uint _approverId,
uint[] memory _tokenAmounts)
public
senderIsValid(_sender)
{
// first fulfills the bounty on behalf of the fulfillers
fulfillBounty(_sender, _bountyId, _fulfillers, _data);
// then accepts the fulfillment
acceptFulfillment(_sender,
_bountyId,
bounties[_bountyId].fulfillments.length - 1,
_approverId,
_tokenAmounts);
}
/// @dev changeBounty(): Allows any of the issuers to change the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _issuers the new array of addresses who will be the issuers of the bounty
/// @param _approvers the new array of addresses who will be the approvers of the bounty
/// @param _data the new IPFS hash representing the JSON object storing the details of the bounty (see docs for schema details)
/// @param _deadline the new timestamp which will become the deadline of the bounty
function changeBounty(
address _sender,
uint _bountyId,
uint _issuerId,
address payable[] memory _issuers,
address payable[] memory _approvers,
string memory _data,
uint _deadline)
public
senderIsValid(_sender)
{
require(_bountyId < numBounties); // makes the validateBountyArrayIndex modifier in-line to avoid stack too deep errors
require(_issuerId < bounties[_bountyId].issuers.length); // makes the validateIssuerArrayIndex modifier in-line to avoid stack too deep errors
require(_sender == bounties[_bountyId].issuers[_issuerId]); // makes the onlyIssuer modifier in-line to avoid stack too deep errors
require(_issuers.length > 0 || _approvers.length > 0); // Ensures there's at least 1 issuer or approver, so funds don't get stuck
bounties[_bountyId].issuers = _issuers;
bounties[_bountyId].approvers = _approvers;
bounties[_bountyId].deadline = _deadline;
emit BountyChanged(_bountyId,
_sender,
_issuers,
_approvers,
_data,
_deadline);
}
/// @dev changeIssuer(): Allows any of the issuers to change a particular issuer of the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _issuerIdToChange the index of the issuer who is being changed
/// @param _newIssuer the address of the new issuer
function changeIssuer(
address _sender,
uint _bountyId,
uint _issuerId,
uint _issuerIdToChange,
address payable _newIssuer)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateIssuerArrayIndex(_bountyId, _issuerIdToChange)
onlyIssuer(_sender, _bountyId, _issuerId)
{
require(_issuerId < bounties[_bountyId].issuers.length || _issuerId == 0);
bounties[_bountyId].issuers[_issuerIdToChange] = _newIssuer;
emit BountyIssuersUpdated(_bountyId, _sender, bounties[_bountyId].issuers);
}
/// @dev changeApprover(): Allows any of the issuers to change a particular approver of the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _approverId the index of the approver who is being changed
/// @param _approver the address of the new approver
function changeApprover(
address _sender,
uint _bountyId,
uint _issuerId,
uint _approverId,
address payable _approver)
external
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
onlyIssuer(_sender, _bountyId, _issuerId)
validateApproverArrayIndex(_bountyId, _approverId)
{
bounties[_bountyId].approvers[_approverId] = _approver;
emit BountyApproversUpdated(_bountyId, _sender, bounties[_bountyId].approvers);
}
/// @dev changeIssuerAndApprover(): Allows any of the issuers to change a particular approver of the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _issuerIdToChange the index of the issuer who is being changed
/// @param _approverIdToChange the index of the approver who is being changed
/// @param _issuer the address of the new approver
function changeIssuerAndApprover(
address _sender,
uint _bountyId,
uint _issuerId,
uint _issuerIdToChange,
uint _approverIdToChange,
address payable _issuer)
external
senderIsValid(_sender)
onlyIssuer(_sender, _bountyId, _issuerId)
{
require(_bountyId < numBounties);
require(_approverIdToChange < bounties[_bountyId].approvers.length);
require(_issuerIdToChange < bounties[_bountyId].issuers.length);
bounties[_bountyId].issuers[_issuerIdToChange] = _issuer;
bounties[_bountyId].approvers[_approverIdToChange] = _issuer;
emit BountyIssuersUpdated(_bountyId, _sender, bounties[_bountyId].issuers);
emit BountyApproversUpdated(_bountyId, _sender, bounties[_bountyId].approvers);
}
/// @dev changeData(): Allows any of the issuers to change the data the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _data the new IPFS hash representing the JSON object storing the details of the bounty (see docs for schema details)
function changeData(
address _sender,
uint _bountyId,
uint _issuerId,
string memory _data)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateIssuerArrayIndex(_bountyId, _issuerId)
onlyIssuer(_sender, _bountyId, _issuerId)
{
emit BountyDataChanged(_bountyId, _sender, _data); // The new _data is emitted within an event rather than being stored on-chain for minimized gas costs
}
/// @dev changeDeadline(): Allows any of the issuers to change the deadline the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _deadline the new timestamp which will become the deadline of the bounty
function changeDeadline(
address _sender,
uint _bountyId,
uint _issuerId,
uint _deadline)
external
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateIssuerArrayIndex(_bountyId, _issuerId)
onlyIssuer(_sender, _bountyId, _issuerId)
{
bounties[_bountyId].deadline = _deadline;
emit BountyDeadlineChanged(_bountyId, _sender, _deadline);
}
/// @dev addIssuers(): Allows any of the issuers to add more issuers to the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _issuers the array of addresses to add to the list of valid issuers
function addIssuers(
address _sender,
uint _bountyId,
uint _issuerId,
address payable[] memory _issuers)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateIssuerArrayIndex(_bountyId, _issuerId)
onlyIssuer(_sender, _bountyId, _issuerId)
{
for (uint i = 0; i < _issuers.length; i++){
bounties[_bountyId].issuers.push(_issuers[i]);
}
emit BountyIssuersUpdated(_bountyId, _sender, bounties[_bountyId].issuers);
}
/// @dev addApprovers(): Allows any of the issuers to add more approvers to the bounty
/// @param _sender the sender of the transaction issuing the bounty (should be the same as msg.sender unless the txn is called by the meta tx relayer)
/// @param _bountyId the index of the bounty
/// @param _issuerId the index of the issuer who is calling the function
/// @param _approvers the array of addresses to add to the list of valid approvers
function addApprovers(
address _sender,
uint _bountyId,
uint _issuerId,
address[] memory _approvers)
public
senderIsValid(_sender)
validateBountyArrayIndex(_bountyId)
validateIssuerArrayIndex(_bountyId, _issuerId)
onlyIssuer(_sender, _bountyId, _issuerId)
{
for (uint i = 0; i < _approvers.length; i++){
bounties[_bountyId].approvers.push(_approvers[i]);
}
emit BountyApproversUpdated(_bountyId, _sender, bounties[_bountyId].approvers);
}
/// @dev getBounty(): Returns the details of the bounty
/// @param _bountyId the index of the bounty
/// @return Returns a tuple for the bounty
function getBounty(uint _bountyId)
external
view
returns (Bounty memory)
{
return bounties[_bountyId];
}
function transferTokens(uint _bountyId, address payable _to, uint _amount)
internal
{
if (bounties[_bountyId].tokenVersion == 0){
require(_amount > 0); // Sending 0 tokens should throw
require(bounties[_bountyId].balance >= _amount);
bounties[_bountyId].balance = bounties[_bountyId].balance.sub(_amount);
_to.transfer(_amount);
} else if (bounties[_bountyId].tokenVersion == 20){
require(_amount > 0); // Sending 0 tokens should throw
require(bounties[_bountyId].balance >= _amount);
bounties[_bountyId].balance = bounties[_bountyId].balance.sub(_amount);
require(ERC20Token(bounties[_bountyId].token).transfer(_to, _amount));
} else if (bounties[_bountyId].tokenVersion == 721){
require(tokenBalances[_bountyId][_amount]);
tokenBalances[_bountyId][_amount] = false; // Removes the 721 token from the balance of the bounty
ERC721BasicToken(bounties[_bountyId].token).transferFrom(address(this),
_to,
_amount);
} else {
revert();
}
}
/*
* Events
*/
event BountyIssued(uint _bountyId, address payable _creator, address payable[] _issuers, address[] _approvers, string _data, uint _deadline, address _token, uint _tokenVersion);
event ContributionAdded(uint _bountyId, uint _contributionId, address payable _contributor, uint _amount);
event ContributionRefunded(uint _bountyId, uint _contributionId);
event ContributionsRefunded(uint _bountyId, address _issuer, uint[] _contributionIds);
event BountyDrained(uint _bountyId, address _issuer, uint[] _amounts);
event ActionPerformed(uint _bountyId, address _fulfiller, string _data);
event BountyFulfilled(uint _bountyId, uint _fulfillmentId, address payable[] _fulfillers, string _data, address _submitter);
event FulfillmentUpdated(uint _bountyId, uint _fulfillmentId, address payable[] _fulfillers, string _data);
event FulfillmentAccepted(uint _bountyId, uint _fulfillmentId, address _approver, uint[] _tokenAmounts);
event BountyChanged(uint _bountyId, address _changer, address payable[] _issuers, address payable[] _approvers, string _data, uint _deadline);
event BountyIssuersUpdated(uint _bountyId, address _changer, address payable[] _issuers);
event BountyApproversUpdated(uint _bountyId, address _changer, address[] _approvers);
event BountyDataChanged(uint _bountyId, address _changer, string _data);
event BountyDeadlineChanged(uint _bountyId, address _changer, uint _deadline);
}
{
"compilationTarget": {
"StandardBounties.sol": "StandardBounties"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_fulfiller","type":"address"},{"indexed":false,"internalType":"string","name":"_data","type":"string"}],"name":"ActionPerformed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_changer","type":"address"},{"indexed":false,"internalType":"address[]","name":"_approvers","type":"address[]"}],"name":"BountyApproversUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_changer","type":"address"},{"indexed":false,"internalType":"address payable[]","name":"_issuers","type":"address[]"},{"indexed":false,"internalType":"address payable[]","name":"_approvers","type":"address[]"},{"indexed":false,"internalType":"string","name":"_data","type":"string"},{"indexed":false,"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"BountyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_changer","type":"address"},{"indexed":false,"internalType":"string","name":"_data","type":"string"}],"name":"BountyDataChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_changer","type":"address"},{"indexed":false,"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"BountyDeadlineChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_issuer","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"BountyDrained","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_fulfillmentId","type":"uint256"},{"indexed":false,"internalType":"address payable[]","name":"_fulfillers","type":"address[]"},{"indexed":false,"internalType":"string","name":"_data","type":"string"},{"indexed":false,"internalType":"address","name":"_submitter","type":"address"}],"name":"BountyFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address payable","name":"_creator","type":"address"},{"indexed":false,"internalType":"address payable[]","name":"_issuers","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"_approvers","type":"address[]"},{"indexed":false,"internalType":"string","name":"_data","type":"string"},{"indexed":false,"internalType":"uint256","name":"_deadline","type":"uint256"},{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_tokenVersion","type":"uint256"}],"name":"BountyIssued","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_changer","type":"address"},{"indexed":false,"internalType":"address payable[]","name":"_issuers","type":"address[]"}],"name":"BountyIssuersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_contributionId","type":"uint256"},{"indexed":false,"internalType":"address payable","name":"_contributor","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"ContributionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_contributionId","type":"uint256"}],"name":"ContributionRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_issuer","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_contributionIds","type":"uint256[]"}],"name":"ContributionsRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_fulfillmentId","type":"uint256"},{"indexed":false,"internalType":"address","name":"_approver","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_tokenAmounts","type":"uint256[]"}],"name":"FulfillmentAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_fulfillmentId","type":"uint256"},{"indexed":false,"internalType":"address payable[]","name":"_fulfillers","type":"address[]"},{"indexed":false,"internalType":"string","name":"_data","type":"string"}],"name":"FulfillmentUpdated","type":"event"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_fulfillmentId","type":"uint256"},{"internalType":"uint256","name":"_approverId","type":"uint256"},{"internalType":"uint256[]","name":"_tokenAmounts","type":"uint256[]"}],"name":"acceptFulfillment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"address[]","name":"_approvers","type":"address[]"}],"name":"addApprovers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"address payable[]","name":"_issuers","type":"address[]"}],"name":"addIssuers","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bounties","outputs":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenVersion","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"bool","name":"hasPaidOut","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"callStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256","name":"_approverId","type":"uint256"},{"internalType":"address payable","name":"_approver","type":"address"}],"name":"changeApprover","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"address payable[]","name":"_issuers","type":"address[]"},{"internalType":"address payable[]","name":"_approvers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"changeBounty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"changeData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"changeDeadline","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256","name":"_issuerIdToChange","type":"uint256"},{"internalType":"address payable","name":"_newIssuer","type":"address"}],"name":"changeIssuer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256","name":"_issuerIdToChange","type":"uint256"},{"internalType":"uint256","name":"_approverIdToChange","type":"uint256"},{"internalType":"address payable","name":"_issuer","type":"address"}],"name":"changeIssuerAndApprover","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"contribute","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"drainBounty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"address payable[]","name":"_fulfillers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"uint256","name":"_approverId","type":"uint256"},{"internalType":"uint256[]","name":"_tokenAmounts","type":"uint256[]"}],"name":"fulfillAndAccept","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"address payable[]","name":"_fulfillers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"}],"name":"fulfillBounty","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_bountyId","type":"uint256"}],"name":"getBounty","outputs":[{"components":[{"internalType":"address payable[]","name":"issuers","type":"address[]"},{"internalType":"address[]","name":"approvers","type":"address[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenVersion","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"bool","name":"hasPaidOut","type":"bool"},{"components":[{"internalType":"address payable[]","name":"fulfillers","type":"address[]"},{"internalType":"address","name":"submitter","type":"address"}],"internalType":"struct StandardBounties.Fulfillment[]","name":"fulfillments","type":"tuple[]"},{"components":[{"internalType":"address payable","name":"contributor","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"refunded","type":"bool"}],"internalType":"struct StandardBounties.Contribution[]","name":"contributions","type":"tuple[]"}],"internalType":"struct StandardBounties.Bounty","name":"","type":"tuple"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_sender","type":"address"},{"internalType":"address payable[]","name":"_issuers","type":"address[]"},{"internalType":"address[]","name":"_approvers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenVersion","type":"uint256"},{"internalType":"uint256","name":"_depositAmount","type":"uint256"}],"name":"issueAndContribute","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"_sender","type":"address"},{"internalType":"address payable[]","name":"_issuers","type":"address[]"},{"internalType":"address[]","name":"_approvers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenVersion","type":"uint256"}],"name":"issueBounty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"metaTxRelayer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"numBounties","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"string","name":"_data","type":"string"}],"name":"performAction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_contributionId","type":"uint256"}],"name":"refundContribution","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_issuerId","type":"uint256"},{"internalType":"uint256[]","name":"_contributionIds","type":"uint256[]"}],"name":"refundContributions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256[]","name":"_contributionIds","type":"uint256[]"}],"name":"refundMyContributions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_relayer","type":"address"}],"name":"setMetaTxRelayer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenBalances","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint256","name":"_fulfillmentId","type":"uint256"},{"internalType":"address payable[]","name":"_fulfillers","type":"address[]"},{"internalType":"string","name":"_data","type":"string"}],"name":"updateFulfillment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]