Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Creating Open Work

A task (bounty) is the unit of open work in the Build Together protocol — a scoped piece of work with an ETH reward held in escrow that any eligible contributor can complete.


What a Task Is

A task combines:

  • A scope — what needs to be built, fixed, or reviewed
  • A reward — ETH promised at creation time, funded into escrow by anyone
  • An onchain record — the BountyEscrow contract holds funds and enforces payout on acceptance

Tasks live onchain. The reward is escrowed independently of creation — contributors can see both the promised amount and the actual funded amount before they start working.

Who Can Create Tasks

Task creation is permission-gated at the contract level. Only the following wallets can call createBounty:

  • Project owner — the address that called createProject
  • Maintainers — wallets added via addMaintainer
  • Allowlisted wallets — wallets added via updateAllowlist

Strangers are rejected with NotAuthorized at the contract level, not just in the UI.

Creating a Task

From the project detail page, click New Task (visible only when you have permission).

The form requires:

  • Title — a short, specific description of the work
  • Description — context, acceptance criteria, constraints (optional but recommended)
  • Reward (ETH) — the target reward amount in wei/ETH. This is the rewardAmount stored onchain.

The app broadcasts BountyEscrow.createBounty(projectId, rewardAmount). The promised reward does not need to be deposited at creation — anyone can fund it later via fundBounty.

Writing a Good Task Spec

A well-written description reduces ambiguity and attracts higher-quality contributions. Include:

Context — why this work matters, what it unblocks, the current state.

Deliverables — concrete list of what "done" looks like. Prefer specific outputs over vague descriptions.

Constraints — stack requirements, things the solution must not do.

Acceptance criteria — how a maintainer will judge whether the work is complete. Automated tests, visual specs, or documented behaviour all work.

Out of scope — explicitly list related work that is not part of this task.

Example:

## Context
The auth flow doesn't handle expired sessions gracefully...
 
## Deliverables
- [ ] Session expiry detected and user redirected to /login
- [ ] Redirect preserves the original destination URL
- [ ] Unit tests covering the redirect logic
 
## Constraints
- Must use the existing auth module (services/api/src/lib/siwe.ts)
- No new dependencies
 
## Acceptance Criteria
All existing auth tests pass, new tests pass, manual QA confirms redirect works.

Funding a Task

Anyone can add ETH to any open or funded bounty:

BountyEscrow.fundBounty(bountyId)  // payable

The bounty status moves from OPEN to FUNDED on the first deposit. Multiple funders can contribute to the same bounty — the full fundedAmount goes to the contributor on acceptance.

Task Lifecycle

OPEN  -->  FUNDED  -->  ACCEPTED  (contributor paid, bounty closed)
  |                         |
  +-----> CANCELLED <-------+   (ETH refunded to creator)
StatusMeaning
OPENCreated onchain, no ETH deposited yet
FUNDEDETH in escrow, accepting contributions
ACCEPTEDContribution accepted, ETH paid to contributor
CANCELLEDCancelled by owner or maintainer, ETH refunded

Accepting a Contribution

When a contributor submits a patch via Radicle, the project owner or any maintainer reviews it and calls:

BountyEscrow.acceptContribution(
  bountyId,
  contributorAddress,
  radicleOid  // Radicle patch/commit identifier
)

The full fundedAmount is transferred to contributorAddress in the same transaction.