mirror of https://github.com/status-im/EIPs.git
126 lines
11 KiB
Markdown
126 lines
11 KiB
Markdown
---
|
|
eip: 1081
|
|
Title: Standard Bounties
|
|
Authors: Mark Beylin <mark.beylin@consensys.net>, Kevin Owocki <kevin.owocki@consensys.net>, Ricardo Guilherme Schmidt (@3esmit)
|
|
Discussions-to: https://gitter.im/bounties-network/Lobby
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Category: ERC
|
|
Created: 2018-05-14
|
|
Requires: 20
|
|
---
|
|
|
|
## Simple Summary
|
|
<!--"If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the EIP.-->
|
|
A standard contract and interface for issuing bounties on Ethereum, usable for any type of task, paying in any ERC20 token or in ETH.
|
|
|
|
## Abstract
|
|
<!--A short (~200 word) description of the technical issue being addressed.-->
|
|
In order to encourage cross-platform interoperability of bounties on Ethereum, and for easier reputational tracking, StandardBounties can facilitate the administration of funds in exchange for deliverables corresponding to a completed task, in a publicly auditable and immutable fashion.
|
|
|
|
## Motivation
|
|
In the absence of a standard for bounties on Ethereum, it would be difficult for platforms to collaborate and share the bounties which users create (thereby recreating the walled gardens which currently exist on Web2.0 task outsourcing platforms). A standardization of these interactions across task types also makes it far easier to track various reputational metrics (such as how frequently you pay for completed submissions, or how frequently your work gets accepted).
|
|
|
|
## Specification
|
|
After studying bounties as they've existed for thousands of years (and after implementing and processing over 300 of them on main-net in beta), we've discovered that there are 3 core steps to every bounty:
|
|
- a bounty is **issued**: an `issuer` specifies the requirements for the task, describing the desired outcome, and how much they would be willing to pay for the completion of that task (denoted in one or several tokens).
|
|
- a bounty is **fulfilled**: a bounty `fulfiller` may see the bounty, complete the task, and produce a deliverable which is itself the desired outcome of the task, or simply a record that it was completed. Hashes of these deliverables should be stored immutably on-chain, to serve as proof after the fact.
|
|
- a fulfillment is **accepted**: a bounty `issuer` or `arbiter` may select one or more submissions to be accepted, thereby releasing payment to the bounty fulfiller(s), and transferring ownership over the given deliverable to the `issuer`.
|
|
|
|
To implement these steps, a number of functions are needed:
|
|
- `initializeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This is used when deploying a new StandardBounty contract, and is particularly useful when applying the proxy design pattern, whereby bounties cannot be initialized in their constructors. Here, the data string should represent an IPFS hash, corresponding to a JSON object which conforms to the schema (described below).
|
|
- `fulfillBounty(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data)`: This is called to submit a fulfillment, submitting a string representing an IPFS hash which contains the deliverable for the bounty. Initially fulfillments could only be submitted by one individual at a time, however users consistently told us they desired to be able to collaborate on fulfillments, thereby allowing the credit for submissions to be shared by several parties. The lines along which eventual payouts are split are determined by the fractions of the submission credited to each fulfiller (using the array of numerators and single denominator). Here, a bounty platform may also include themselves as a collaborator to collect a small fee for matching the bounty with fulfillers.
|
|
- `acceptFulfillment(uint _fulfillmentId, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: This is called by the `issuer` or the `arbiter` to pay out a given fulfillment, using an array of tokens, and an array of amounts of each token to be split among the contributors. This allows for the bounty payout amount to move as it needs to based on incoming contributions (which may be transferred directly to the contract address). It also allows for the easy splitting of a given bounty's balance among several fulfillments, if the need should arise.
|
|
- `drainBounty(StandardToken[] _payoutTokens)`: This may be called by the `issuer` to drain a bounty of it's funds, if the need should arise.
|
|
- `changeBounty(address _issuer, address _arbiter, string _data, uint _deadline)`: This may be called by the `issuer` to change the `issuer`, `arbiter`, `data`, and `deadline` fields of their bounty.
|
|
- `changeIssuer(address _issuer)`: This may be called by the `issuer` to change to a new `issuer` if need be
|
|
- `changeArbiter(address _arbiter)`: This may be called by the `issuer` to change to a new `arbiter` if need be
|
|
- `changeData(string _data)`: This may be called by the `issuer` to change just the `data`
|
|
- `changeDeadline(uint _deadline)`: This may be called by the `issuer` to change just the `deadline`
|
|
|
|
Optional Functions:
|
|
- `acceptAndFulfill(address[] _fulfillers, uint[] _numerators, uint _denomenator, string _data, StandardToken[] _payoutTokens, uint[] _tokenAmounts)`: During the course of the development of this standard, we discovered the desire for fulfillers to avoid paying gas fees on their own, entrusting the bounty's `issuer` to make the submission for them, and at the same time accept it. This is useful since it still immutably stores the exchange of tokens for completed work, but avoids the need for new bounty fulfillers to have any ETH to pay for gas costs in advance of their earnings.
|
|
- `changeMasterCopy(StandardBounty _masterCopy)`: For `issuer`s to be able to change the masterCopy which their proxy contract relies on, if the proxy design pattern is being employed.
|
|
- `refundableContribute(uint[] _amounts, StandardToken[] _tokens)`: While non-refundable contributions may be sent to a bounty simply by transferring those tokens to the address where it resides, one may also desire to contribute to a bounty with the option to refund their contribution, should the bounty never receive a correct submission which is paid out.
|
|
`refundContribution(uint _contributionId)`: If a bounty hasn't yet paid out to any correct submissions and is past it's deadline, those individuals who employed the `refundableContribute` function may retreive their funds from the contract.
|
|
|
|
**Schemas**
|
|
Persona Schema:
|
|
```
|
|
{
|
|
name: // optional - A string representing the name of the persona
|
|
email: // optional - A string representing the preferred contact email of the persona
|
|
githubUsername: // optional - A string representing the github username of the persona
|
|
address: // required - A string web3 address of the persona
|
|
}
|
|
```
|
|
Bounty issuance `data` Schema:
|
|
```
|
|
{
|
|
payload: {
|
|
title: // A string representing the title of the bounty
|
|
description: // A string representing the description of the bounty, including all requirements
|
|
issuer: {
|
|
// persona for the issuer of the bounty
|
|
},
|
|
funders:[
|
|
// array of personas of those who funded the issue.
|
|
],
|
|
categories: // an array of strings, representing the categories of tasks which are being requested
|
|
created: // the timestamp in seconds when the bounty was created
|
|
tokenSymbol: // the symbol for the token which the bounty pays out
|
|
tokenAddress: // the address for the token which the bounty pays out (0x0 if ETH)
|
|
|
|
// ------- add optional fields here -------
|
|
sourceFileName: // A string representing the name of the file
|
|
sourceFileHash: // The IPFS hash of the file associated with the bounty
|
|
sourceDirectoryHash: // The IPFS hash of the directory which can be used to access the file
|
|
webReferenceURL: // The link to a relevant web reference (ie github issue)
|
|
},
|
|
meta: {
|
|
platform: // a string representing the original posting platform (ie 'gitcoin')
|
|
schemaVersion: // a string representing the version number (ie '0.1')
|
|
schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema')
|
|
}
|
|
}
|
|
```
|
|
Bounty `fulfillment` data Schema:
|
|
|
|
```
|
|
{
|
|
payload: {
|
|
description: // A string representing the description of the fulfillment, and any necessary links to works
|
|
sourceFileName: // A string representing the name of the file being submitted
|
|
sourceFileHash: // A string representing the IPFS hash of the file being submitted
|
|
sourceDirectoryHash: // A string representing the IPFS hash of the directory which holds the file being submitted
|
|
fulfillers: {
|
|
// personas for the individuals whose work is being submitted
|
|
}
|
|
|
|
// ------- add optional fields here -------
|
|
},
|
|
meta: {
|
|
platform: // a string representing the original posting platform (ie 'gitcoin')
|
|
schemaVersion: // a string representing the version number (ie '0.1')
|
|
schemaName: // a string representing the name of the schema (ie 'standardSchema' or 'gitcoinSchema')
|
|
}
|
|
}
|
|
```
|
|
## Rationale
|
|
<!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.-->
|
|
The development of this standard began a year ago, with the goal of encouraging interoperability among bounty implementations on Ethereum. The initial version had significantly more restrictions: a bounty's `data` could not be changed after issuance (it seemed unfair for bounty `issuer`s to change the requirements after work is underway), and the bounty payout could not be changed (all funds needed to be deposited in the bounty contract before it could accept submissions).
|
|
|
|
The initial version was also far less extensible, and only allowed for fixed payments to a given set of fulfillments. This new version makes it possible for funds to be split among several correct submissions, for submissions to be shared among several contributors, and for payouts to not only be in a single token as before, but in as many tokens as the `issuer` of the bounty desires. These design decisions were made after the 8+ months which Gitcoin, the Bounties Network, and Status Open Bounty have been live and meaningfully facilitating bounties for repositories in the Web3.0 ecosystem.
|
|
|
|
## Test Cases
|
|
<!--Test cases for an implementation are mandatory for EIPs that are affecting consensus changes. Other EIPs can choose to include links to test cases if applicable.-->
|
|
Tests for our implementation can be found here: https://github.com/Bounties-Network/StandardBounties/tree/develop/test
|
|
|
|
## Implementation
|
|
<!--The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.-->
|
|
A reference implementation can be found here: https://github.com/Bounties-Network/StandardBounties/blob/develop/contracts/StandardBounty.sol
|
|
**Although this code has been tested, it has not yet been audited or bug-bountied, so we cannot make any assertions about it's correctness, nor can we presently encourage it's use to hold funds on the Ethereum mainnet.**
|
|
|
|
## Copyright
|
|
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|