Grants market — gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/grants
A decentralized grants market on pearl-1: creators escrow GNOT behind a grant with an application deadline, applicants apply on-chain, the creator selects one applicant, the winner claims the funding minus a transparent protocol fee. Built as the open-ended application engineering benchmark: product spec → ecosystem discovery → reuse selection → architecture → implementation → testing → adversarial audit → pearl-1 validation.
DISCOVERY / REUSE ANALYSIS (summary; full record in commit 1ff98ff)
Searched: fresh vm/qpaths enumeration of pearl-1 (190 /p/ + 434 /r/), keyword sweeps (grant/crowd/milestone/fund/applic/escrow/market/...), outline inspection of the four closest candidates, the pinned gnolang/gno examples tree, and the Cosmic Bull catalog. Findings: no relevant existing implementation was found in the searched sources for an open-application grants market. Closest neighbors, both RELATED/unsuitable: r/samcrew/escrow_v3 (serious bilateral milestone escrow — counterparty fixed at creation, no application phase) and r/g16m0r7.../bountyboard (identity passed as string parameters). Reused: p/.../feeledger (all balance accounting), p/.../coinio (all coin movement), ecosystem p/nt/markdown/sanitize/v0 (free-text render safety — the case where an ecosystem primitive beats anything of ours), p/nt/avl/v0. Genuinely new: exactly one realm — the grants state machine. No new /p/ package was needed. Search limits: keyword/path matching on one chain plus the pinned tree; no GitHub-wide sweep; the chain grows continuously.
Architecture & state ownership
| Concern | Owner |
|---|---|
| Grant records, per-grant applications, openTotal (G), deadlines, roles | this realm |
| Claimable balances (U), fee pot (F), fee arithmetic, overflow checks | feeledger (deployed instance) |
| Receipt guard, payouts, reserve-protected sweeps | coinio (deployed instance) |
| Free-text markdown neutralization in Render | p/nt/markdown/sanitize/v0 |
Lifecycle: Open -> Awarded | Cancelled | Expired (terminal, exactly one transition). Applications keyed by the runtime-derived caller address — altering another user's application or claiming another user's grant is impossible by construction. The creator cannot apply to their own grant; SelectWinner accepts only addresses that applied. Deadline (block height, fixed at creation) gates applications only; ExpireGrant is callable by ANYONE after deadline + 100 blocks — the permissionless valve that makes fund-trapping impossible.
Invariant specification
Monetary (conservation): H == G + U + F + S, S >= 0 — coins held at the realm address equal open escrow + claimable balances + fee pot
- out-of-band surplus. Asserted in delta form at phase boundaries and
at the end of every test in the suite, and verified live against independent auth/accounts queries at four checkpoints.
Application: openTotal == Σ amount(status == Open); winner set iff Awarded and winner applied; deadline immutable.
Fee model: fee = floor(amount × bps / 10000), rounding favors the winner; snapshotted at creation (award-time races closed) AND capped by the creator's own maxFeeBps argument (creation-time races closed — audit finding Y1); compile-time cap 10%; refunds always fee-free.
Security / audit
Fresh-context gno-auditor review at commit 1ff98ff: 0 RED, 1 YELLOW (the creation-time fee race) — fixed structurally with the ceiling parameter + regression tests; the audit's test-adequacy gaps closed (terminal matrix, post-grace ordering, zero-address winner, boundary inputs, render pagination, zero-pot paths). The sanitizer boundary was verified against the DEPLOYED pearl-1 source with live behavioral probes (links, images, HTML, headings, gno-form tags all neutralized). 23 realm tests; 79 workspace tests.
Limitations
- Roles are hot testnet agent keys; one-step TransferAdmin.
- ExpiryGraceBlocks=100 (~8 min) is a TESTNET setting; production would raise it substantially.
- Grant/application records are never pruned (storage paid by their creators; deposit-refund economics make sweeping possible later).
- Applications are text pitches only; no milestone/tranche payouts — see escrow_v3 for that domain.
- Post-grace, a grant is both award-able and expire-able; whichever transaction lands first wins (documented race, no fund loss either way).
Dependency graph
grants ──┬─> coinio ────> (stdlib)
├─> feeledger ─> p/nt/avl/v0
├─> p/nt/avl/v0
└─> p/nt/markdown/sanitize/v0
Deployment record: pearl/DEPLOYMENT.md §5.