Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

bounty_panel package

Overview

Realm bounty_panel is a public bounty board whose resolution authority is separated from its funding authority.

A funder escrows GNOT behind a bounty and, AT CREATION, names a panel of resolvers and an M-of-N threshold. Contributors submit work on-chain during a submission window. After that window closes the panel votes on the competing submissions, and the first submission to reach M votes wins the escrow. Neither the panel nor the threshold can change after creation.

WHY THIS EXISTS (see DISCOVERY.md): the sibling realm `grants` already implements escrowed rewards, on-chain submissions, restricted award and a permissionless refund valve — but there the resolver IS the funder. On a public bounty with open submissions that is the wrong trust model: the deciding party has a financial interest in the outcome and sees every submission before deciding. This realm exists for exactly that delta and reuses everything else.

COMPOSITION: all balance accounting is delegated to feeledger, all coin movement to coinio, and all free-text render output to the ecosystem sanitizer p/nt/markdown/sanitize/v0. This realm owns only the bounty state machine: bounty records, panels, submissions, votes, open-escrow total, deadlines, and roles.

LIFECYCLE (terminal states are frozen; one transition per bounty):

Example
 1CreateBounty (EOA + -send)  : escrow -> openTotal, status Open;
 2                              panel + threshold + fee bps all
 3                              SNAPSHOTTED at creation
 4Submit (also re-submit)     : while Open and height <
 5                              submitDeadline; keyed by the caller's
 6                              own address; funder and panel barred
 7Vote (panel only)           : while Open and submitDeadline <=
 8                              height < resolveDeadline; one live
 9                              vote per resolver, changeable until
10                              the threshold is reached; the Mth vote
11                              for a submission awards the bounty
12                              ATOMICALLY (Open -> Awarded)
13CancelBounty (funder only)  : Open -> Cancelled, fee-free refund —
14                              ONLY while no submission exists
15ExpireBounty (ANYONE)       : Open -> Expired once height >=
16                              resolveDeadline + ExpiryGraceBlocks;
17                              fee-free refund to the funder — the
18                              permissionless valve against a panel
19                              that never resolves (but see THE ONE
20                              CAVEAT below)
21Claim / ClaimAll (anyone)   : pays out the caller's own ledger
22                              balance (winnings and refunds)
23WithdrawFees (fee recipient): pays out the fee pot

WHY THE WINDOWS DO NOT OVERLAP: submissions close at submitDeadline and voting opens at the same height. A resolver therefore votes only on content that can no longer change, which removes the bait-and- switch where a submission collects votes and is then edited. It also means no submission can be added in response to the votes already cast.

THE ONE CAVEAT ON THE EXPIRY VALVE, stated rather than glossed: both ways out of an Open bounty — award and refund — credit the shared feeledger, so both fail while that ledger is saturated at the int64 boundary, and the escrow is temporarily immovable in BOTH directions until some account claims down. Nothing is lost and the valve works again as soon as the ledger has headroom (this is exercised in TestSaturatedAwardCannotTrapFunds). The state requires liabilities within ~50 of 2^63-1 ugnot, which exceeds the real GNOT supply by orders of magnitude and is unreachable absent a chain-level minting bug, since every credit is backed by an escrowed -send. So: the valve makes fund-trapping impossible under any reachable condition, which is a weaker claim than "impossible" and is the true one.

WHY CANCEL IS RESTRICTED: in `grants` the creator may cancel at any time while open. Here, once a single contributor has submitted work, the funder can no longer unilaterally reclaim the escrow — only the panel (by awarding) or the expiry valve (after the resolution window) can end the bounty. This is the concrete anti-harvest guarantee that a public bounty needs and a grant programme does not.

AUTHORIZATION: every identity is derived from the crossing entrypoint's cur.Previous().Address() — no function takes a caller identity as a parameter. Submissions, votes and claimable balances are keyed by that runtime-derived address, so altering another user's submission, casting another resolver's vote, or claiming another user's winnings is impossible by construction.

PANEL INTEGRITY, fixed at creation and immutable thereafter: the panel is non-empty, free of duplicates, every member is a valid bech32 address, and 1 <= threshold <= panelSize. Panel members may not submit work, so a resolver cannot vote for their own submission. The funder MAY be a panel member — barring them would be unenforceable theatre (a funder can always name an address they control), and the panel is public on-chain from creation, so a self-resolved bounty is visible to contributors BEFORE they spend effort. Disclosure beats a prohibition that cannot be enforced.

FEE MODEL: fee = floor(amount * bps / 10000), rounding favors the winner; no minimum fee; bps snapshotted into the bounty at creation, so SetFeeBps affects future bounties only (closes the award-time admin race), and CreateBounty takes the caller's own maxFeeBps ceiling, rejecting creation if the live fee exceeds what the funder signed for (closes the creation-time race); hard compile-time cap MaxFeeBps (10%); refunds (cancel/expire) are always fee-free.

MONETARY INVARIANT (conservation): let H be ugnot held at this realm's address, B = openTotal (Σ amount over Open bounties), U the ledger's claimable balances, F the fee pot, S >= 0 out-of-band surplus:

Example
1H == B + U + F + S

Every transition moves value between exactly two terms inside one transaction: CreateBounty raises H and B together (coinio.Receive is the receipt-guaranteed shape); award/cancel/expire move amount from B into U+F with feeledger guaranteeing credited + fee == amount; claims and fee withdrawal debit the ledger before coinio.Payout moves the identical amount out (checks-effects-interactions); any panic aborts the whole transaction; this realm never issues or removes coins. Surplus is recoverable only via SweepDenom (fee recipient), which reserves Liabilities() = B + U + F.

ONLY GNOT: CreateBounty rejects any envelope that is not exactly one positive ugnot coin (coinio.Receive). Every other entrypoint rejects attached coins outright rather than converting them to sweepable surplus.

Function

SetFeeRecipient

func SetFeeRecipient(cur realm, next address)

SetFeeRecipient re-points the fee/surplus role, including the pot accrued so far. Admin only; zero address rejected.

Param

Command

# WARNING: This command is running in an INSECURE mode.
# It is strongly recommended to use a hardware device for signing
# and avoid trusting any computer connected to the internet,
# as your private keys could be exposed.

gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/bounty_panel" -func "SetFeeRecipient" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -chainid "pearl-1" -remote "https://rpc.pearl.testnets.gno.land" ADDRESSgnokey query -remote "https://rpc.pearl.testnets.gno.land" auth/accounts/ADDRESS
gnokey maketx call -pkgpath "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/bounty_panel" -func "SetFeeRecipient" -args $'' -gas-fee 1000000ugnot -gas-wanted 1_000_000_000 -send "" -broadcast=false ADDRESS > call.tx
gnokey sign -tx-path call.tx -chainid "pearl-1" -account-number ACCOUNTNUMBER -account-sequence SEQUENCENUMBER ADDRESS
gnokey broadcast -remote "https://rpc.pearl.testnets.gno.land" call.tx