Cosmic Bull

Rendered from pearl/r/bounty_panel/DISCOVERY.md at commit f191063fd89d in the project repository. The committed file is the source of truth; this page is a rendering of it.

Discovery — bounty_panel

Mandatory discovery gate (docs/DISCOVERY_AND_REUSE.md) for the application-factory objective:

Build a Gno application that allows people to create public bounties with a reward, contributors to submit work, a designated resolution process to determine the winning submission, and the reward to be paid on-chain to the winner. The application should support the lifecycle from bounty creation through completion and payout, while preventing unauthorized claims, double payouts, and stuck funds.

Run 2026-09-21, before any code was written. This document records what was searched, what was found, how each hit was classified, and what was decided.

The headline finding is a near-duplicate of Cosmic Bull's own live work. It is stated first, because it is the finding that should change what gets built.


1. Headline finding

gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/grantslive on pearl-1 at height 584731 — already satisfies most of the objective as literally written. It is Cosmic Bull's own realm.

Objective clausegrants today
create public bounties with a rewardCreateGrant escrows the reward at creation via coinio.Receive
contributors submit workApply(cur, id, pitch) — height-stamped, re-submittable, creator barred, length-bounded
a designated resolution processcreator decides, unilaterally (SelectWinner)
reward paid on-chain to the winnerfeeledger.Deposit(winner, …) then pull-payment Claim
prevent unauthorized claimswinner must be an applicant; pull-payment keyed to the credited account
prevent double payoutsterminal status set before credit; StatusOpen re-checked
prevent stuck fundsExpireGrant — permissionless refund valve after deadline + ExpiryGraceBlocks

Six of the seven clauses are already implemented, audited, deployed, and verified live, with the conservation identity H == G + U + F + S proven on-chain.

Rebuilding that is duplication, and the gate exists to stop it.

The one clause that is not satisfied

"a designated resolution process to determine the winning submission"

In grants the resolver is the funder, implicitly and unavoidably:

func SelectWinner(cur realm, id int64, winner address) {
	caller := cur.Previous().Address()
	g := mustGetGrant(id)
	if caller != g.creator {
		panic("only the grant creator may select a winner")
	}

The funder is the party with a financial interest in the outcome. On a public bounty with open submissions that is a real problem, not a cosmetic one: the funder sees every submission before deciding, and can award to an address they control, or simply not decide and reclaim the escrow through the expiry valve, having harvested the work.

grants is a grant programme — a funder choosing among pitches is the intended process there, and the realm is correct for what it is. It is the wrong trust model for an adjudicated public bounty.

The delta between the objective and existing work is therefore exactly one thing: resolution authority that is separate from funding authority, designated at creation, and immutable thereafter.


2. Sources searched

#SourceMethodScope reached
1Cosmic Bull catalogreadcatalog/applications.md, catalog/primitives.md, pearl/INFRASTRUCTURE.md, pearl/DEPLOYMENT.md
2Cosmic Bull /p/ and /r/ sourcesread2 primitives + 12 realm trees under pearl/
3pearl-1 live namespacevm/qpaths full enumeration631 packages (190 /p/, 441 /r/)
4On-chain source of candidatesvm/qfile via gno_read11 packages read (outline and/or full)
5Public Gno ecosystemGitHub code search (extension:gno, all public repos) + webgnolang/gno incl. examples/quarantined and r/archive, gnolang/tx-exports (extracted source for pearl/sapphire/topaz/staging), moul/gno-contracts, AmozPay/gbnty, gnolang/workshops, gnoverse/gc24-us-workshop, gno.land bounty-programme docs

Source 3 keyword sweep terms: bount, escrow, reward, grant, quest, task, job, arbit, judge, jury, dispute, review, submi, milestone, hackathon, prize, vote, poll, dao.

Note on drift: the enumeration returned 631 packages against 624 recorded at an earlier audit. The chain grows; discovery is re-run per build and its result is not reusable across builds.


3. Classified findings

COSMIC BULL EXISTING PRIMITIVE — reuse as-is

PackageWhy it is reused
p/…/coinioVerified payment receipt (Receive), disciplined payout (Payout), reserve-protected Sweep. Asserts rlm.IsCurrent(); coins can only move from the calling realm's own address. Exactly the escrow plumbing this objective needs.
p/…/feeledgerPer-account balances, UsersTotal, separately-accrued fee pot, checked-add overflow guards, fee rounding that favours the depositor. Gives the pull-payment ledger and the conservation identity for free.

Both are live on pearl-1 and are consumed by bounties, grants, vault, coindemo and market. No new value-handling primitive is required, and none will be written.

EXACT DUPLICATE — none

No package found implements the objective including a designated resolver distinct from the funder.

Package / repoWhat it hasWhy it is not reused
r/…/grants (Cosmic Bull, live)The whole lifecycle bar the resolverResolver is the funder by construction. Frozen — not modified, not redeployed. Its proven patterns are re-applied; its bytes are not touched.
r/…/bounties (Cosmic Bull, live)Escrow + award + payoutNo submission type at all. Award(cur, id, winner address) takes a raw address gated only by caller != b.funder; nothing links the award to participation. Frozen.
r/g16m0r7…/bountyboard (pearl-1)The closest ecosystem match: Submissions []Submission, a Reviewer field, Review(reviewer, id, approve)Identities are string parameters (CreateBounty(creator, title, reviewer string, …), Claim(hunter, …)) — anyone may pass any identity, the designation-forgery shape. Time comes from a manual Tick(height int64) rather than runtime.ChainHeight(). escrowBalance int64 is a number, not custody — no real coins are held or paid. Unsuitable at the trust-model level, not fixable by adaptation.
r/samcrew/escrow_v3 (pearl-1)Serious, real-custody milestone escrow: admin arbitration, ClaimRefund and ClaimDisputeTimeout permissionless valves, TotalLiabilities() solvency surfaceBilateral and pre-designated. One client, one named freelancer, no open submission and no competition among contributors. Its arbiter is a single global realm admin, not a per-bounty designated panel. Different application. Its dispute-timeout design is good and is cited as precedent for the expiry valve.
r/archive/nir1218_evaluation_proposal/evaluation (gnolang/gno, quarantined)A committee that votes to approve contributions — conceptually the designated resolution processA design sketch, never deployed. No crossing functions and no exported entrypoints; authorization via unsafe.OriginCaller() inside non-crossing methods (security.md Class 2); time.Time fields; threshold hard-coded to unanimity with // TODO Add threshold factor; no value, no payout. Its EVALUATION.md names the exact gap this application fills: "scoring a contribution is essential when there are competing contributions" — listed under Future Improvements, unimplemented.
AmozPay/gbnty (GitHub)A dedicated Gno bounty contractBounty listing only — no escrow, no submissions, no resolution, no payout. Returns error from mutators instead of panicking, so failures do not revert state.
p/g12e22…/commondaoFull DAO: council, proposals, electorate snapshots, tallies, treasuryOver-scoped by an order of magnitude, and its own docs warn that handing an executor a treasury sub is "an irrevocable grant of that DAO's treasury, not a scoped loan of it." Routing a bounty payout through an arbitrary-execution proposal is a strictly worse security posture than a direct ledger credit. Tally is YES/NO on one proposal; deadlines are time.Duration.
p/samcrew/daocondBallot + composable Condition; RoleCount(count, role, …) is literally M-of-NClosest reusable voting primitive, and genuinely considered. Rejected on three grounds: thresholds and signals are float64 (floating point in consensus-critical tallying); conditions are built from caller-supplied closures (hasRoleFn func(string, string) bool) — the callback-parameter shape the Gno security guidance flags, and /p/-declared closures carry creator-realm authority; ballots are keyed by voter string, not address.
r/moul/x/daily/escrow/v1Clean 2-party escrow, correct crossing-function shapeExplicitly accounting only — "no funds move". Bilateral.
r/g1nyjym…/escrow2-party escrowToy: string caller parameters, no custody.
r/samcrew/memba_reviews_v1Ratings, reactions, moderationReview as opinion, not as resolution. Holds no value, selects no winner.
r/samcrew/memba_quest_attestation_v1Offline-signed vouchers attesting quest completionAuthority is an off-chain signer, not an on-chain designated party. No value, no competition.
r/moul/x/daily/polls/v1, nsvote/*One-address-one-vote tallyingOpen public polls. No electorate restriction, no value, no binding outcome.
r/…/reward1, reward2GRC20 tokens named "reward"Keyword collision only.

GENUINELY NEW

The resolution layer: a set of resolvers designated at bounty creation, voting on competing submissions under an M-of-N threshold fixed at creation, with the escrow released only by that threshold.

Two properties make this new rather than a re-skin of existing voting code:

  1. The tally selects among N candidates, it does not approve one proposal. Every voting package found — commondao, daocond, polls, nsvote, the archived committee — tallies YES/NO against a single subject. A bounty resolution asks which submission, and the winner is the first submission to reach the threshold. That is a different tally shape.
  2. The tally is directly coupled to value release. No package found combines an on-chain restricted electorate with real coin custody in the same trust boundary.

No relevant existing implementation was found in the searched sources.

Per the standing rule, that sentence is a statement about the searched sources. It is not a claim of ecosystem-wide uniqueness, and "not found" is not "does not exist."


4. Decisions

  1. Build, but build only the delta. The escrow, fee, pull-payment, conservation and expiry mechanics are reused from coinio + feeledger and from patterns already proven live in bounties and grants. New code is confined to the resolution layer and the state it needs.
  2. No new /p/ primitive. The M-of-N tally is per-bounty integer counting over an AVL tree — on the order of fifty lines. Extracting it would be speculative: a primitive is extracted when two audited realms repeat it, which is how coinio and feeledger were born. One use is not two.
  3. Do not reuse daocond or commondao. Recorded above with reasons. The substituted code is smaller, integer-only, closure-free, and address-keyed.
  4. Do not touch bounties or grants. Both are live and frozen. This is a new realm at a new path. Sibling [addpkg] heights will be re-read after deployment to prove it.
  5. Separate the submission window from the resolution window. Submissions close at a deadline; the panel then has a bounded resolution window; only after that window closes can anyone trigger the refund. This bounds the harvest-and-stall attack in time.
  6. Resolvers may not submit. A resolver voting for their own work is the obvious attack and it is cheaply enforceable, since the resolver set is fixed at creation.
  7. The funder is not barred from the resolver set. Barring them would be security theatre — a funder can name an address they control regardless — and it would give false assurance. The resolver set is public on-chain at creation, so a self-resolved bounty is visible to every contributor before they spend effort. Disclosure beats an unenforceable prohibition.
  8. No forfeit on failed resolution. A refund on panel deadlock returns the escrow to the funder, which leaves a residual griefing path: fund, harvest submissions, stall, reclaim. The obvious mitigation — forfeiting the escrow to the submitters — creates a worse incentive, to spam submissions in order to farm forfeits. Accepted as a documented limitation rather than half-fixed. See §5.

5. Known limitations, recorded before implementation


6. Evidence

Chain reads were performed against pearl-1 through the Gno MCP tools (vm/qpaths for enumeration, vm/qfile via gno_read for source). GitHub searches were performed with gh api search/code. Every classification above rests on source that was read, not on a package name or a doc comment — outlines were used for navigation and whole files for anything a decision turned on.