Skip to content

Lock funds at creation and release them on a verifier's vote, a denial, or a deadline. Three approvals, one escrow address, no contract.

A bounty is funds locked at collection creation plus three ways they can leave: accept (paid to the recipient), deny (refunded to the submitter), and expire (refunded after the deadline). Each exit is a collection approval with a coin transfer from the mint escrow address. A named verifier decides accept or deny by casting one on-chain vote. Nobody, including the creator, can move the money any other way.

The same three-branch shape is a milestone contract, a job escrow, a dispute-resolved trade, or a multi-party agreement with six or twelve branches instead of three.

What Makes It Work

RequirementToken-standard primitive
Funds locked at creationmintEscrowCoinsToTransfer on MsgUniversalUpdateCollection funds the collection's mint escrow address. See Coin Transfers
Escrow pays outcoinTransfers with overrideFromWithApproverAddress: true, so the escrow is the payer
A verifier decidesvotingChallenges with the verifier as the only voter and quorumThreshold: "100". The verifier signs MsgCastVote; anyone can then execute. See Voting Challenges
Refund after the deadline with no action from anyoneAn expire approval whose transferTimes starts one millisecond after the deadline
Each branch fires oncemaxNumTransfers.overallMaxNumTransfers: "1" per approval
Nobody can edit the termsAll permissions locked at creation

The Fields That Matter

json
{
  "mintEscrowCoinsToTransfer": [{ "amount": "500000000", "denom": "ibc/E1116484B327AEE59CDC3DA73D319834781A13DB2A7DFC1F38A30CD45ABF58B8" }],
  "collectionApprovals": [
    {
      "approvalId": "bounty-accept",
      "fromListId": "Mint",
      "toListId": "bb1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs7gvmv",
      "initiatedByListId": "All",
      "transferTimes": [{ "start": "1", "end": "1791331200000" }],
      "approvalCriteria": {
        "votingChallenges": [{ "proposalId": "bounty-accept-3f9a", "quorumThreshold": "100", "voters": [{ "address": "bb1zc268nctj8xwslgw7q22cahs6k4y048agr6fvf", "weight": "1" }] }],
        "coinTransfers": [{ "to": "bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue", "coins": [{ "amount": "500000000", "denom": "ibc/E1116484B327AEE59CDC3DA73D319834781A13DB2A7DFC1F38A30CD45ABF58B8" }], "overrideFromWithApproverAddress": true }],
        "maxNumTransfers": { "overallMaxNumTransfers": "1", "amountTrackerId": "bounty-accept-tracker" }
      }
    },
    {
      "approvalId": "bounty-expire",
      "fromListId": "Mint",
      "toListId": "bb1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqs7gvmv",
      "initiatedByListId": "All",
      "transferTimes": [{ "start": "1791331200001", "end": "18446744073709551615" }],
      "approvalCriteria": {
        "coinTransfers": [{ "to": "bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d", "coins": [{ "amount": "500000000", "denom": "ibc/E1116484B327AEE59CDC3DA73D319834781A13DB2A7DFC1F38A30CD45ABF58B8" }], "overrideFromWithApproverAddress": true }],
        "maxNumTransfers": { "overallMaxNumTransfers": "1", "amountTrackerId": "bounty-expire-tracker" }
      }
    }
  ]
}
bounty-acceptcollectionID: bounty-accept
SenderMint
Recipientbb1qqqqqqq...gvmv
InitiatorAll
Transfer TimesGenesis to Oct 7, 2026
Token IDsAll
Ownership TimesAll time
Verifier vote500 USDC from escrow to bobMax 1 transfer

The accept branch as the transferability tab lists it: anyone can execute it once the verifier has voted, until the deadline.

The bounty-deny approval is bounty-accept with the coin transfer pointed at the submitter. Each branch mints one receipt token straight to the burn address, so the chain records which branch ran.

bash
bb build bounty --amount 500 --denom USDC --verifier bb1zc268nctj8xwslgw7q22cahs6k4y048agr6fvf \
  --recipient bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue --submitter bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d \
  --expiration 30d --uri ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/collection.json

This emits a Bounty collection that moves 500 USDC into escrow when the creator signs, with the accept, deny, and expire approvals above.

Variations

  • Several verifiers: list them in voters with weights and set quorumThreshold: "51" for a majority. See Multi-Sig Voting.
  • Timelock: delayAfterQuorum on the voting challenge holds the payout for a review window after the vote passes.
  • Partial payouts: one accept approval per milestone, each with its own amount, under the ListView:Milestones standard. See Payment Protocol.
  • Long-lived escrow with deposits from many parties: hold the funds as a backed token instead, and write the release, refund, and dispute branches as its withdraw approvals.
  • No escrow at all: use a payment request, where the payer funds the transfer at approval time.

Build It

text
Load the bounty skill. Build a 250 USDC bounty where bb1zc268nctj8xwslgw7q22cahs6k4y048agr6fvf verifies, bb1py4mfpg6uf59qkyzg0nmau322c5873eeysp5ue is paid on accept, I am refunded on deny or after 14 days. Validate, review, simulate, then give me the review link.

Edit this page on GitHub