Realm on pearl-1
grants
gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/grants
realmapplicationcoordination
Grant programme with escrowed disbursement, composing coinio + feeledger + sanitize/v0.
Identity
| Import path | gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/grants |
|---|---|
| Kind | realm (/r/) |
| Chain | pearl-1 |
| Namespace | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Realm address | g10wjaak8z5nhzke76qxmsjdu2rjxcnm5j6zw54m derived, never confirmed against the realm |
Provenance
chain-attested| Deployed at height | 584,731 |
|---|---|
| Deploy transaction | 6093675f15c27702f8c8599309e1a40e5964b96973369547ee11ba301f8a3f60 look it up on the RPC |
| Deployer | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Gas used | 39,004,757 |
| Storage | 39,862 bytes, deposit 3986200ugnot |
| Files on chain | gnomod.toml grants.gno |
| Deployed bytes | grants.gno — 21,364 bytes |
| sha256 | 5c6f94a848530f092179c5b6e3e6fc76c57f834d7134e20d20502f21d9d44e93 |
Do not take the hash above on trust. $download returns the bytes pearl-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:
curl -sS 'https://pearl.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/grants$download&file=grants.gno' | shasum -a 256Expected: 5c6f94a848530f092179c5b6e3e6fc76c57f834d7134e20d20502f21d9d44e93 — 21,364 bytes. This was checked for all 21 packages while building this site's architecture record; every one matched. Use curl: pearl's edge answers Python's default user-agent with HTTP 403.
API
chain-derived 27 exported functions.
Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:
ApplicationOfApplyBalanceOfCancelGrantClaimClaimAllCreateGrantDescriptionExpireGrantFeeBpsFeeOnFeesAccruedGrantInfoHeightHeldLiabilitiesNumGrantsOpenTotalRenderSelectWinnerSetFeeBpsSetFeeRecipientSurplusSweepDenomTransferAdminUsersTotalWithdrawFees
Overview
Realm grants is a decentralized grants market: a creator escrows GNOT behind a grant with an application deadline, applicants apply on-chain, the creator selects one applicant, and the winner claims the funding minus a transparent protocol fee.
COMPOSITION (per the recorded DISCOVERY / REUSE ANALYSIS): 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 grants state machine: grant records, per-grant applications, open-escrow total, deadlines, and roles.
LIFECYCLE (terminal states are frozen; one transition per grant):
CreateGrant (EOA + -send) : escrow -> openTotal, status Open, fee bps SNAPSHOTTED at creation Apply (also re-apply/update) : while Open and height < deadline; keyed by the caller's own address SelectWinner (creator only) : Open -> Awarded; winner MUST be an applicant; escrow -> winner's claimable balance minus the snapshotted fee CancelGrant (creator only) : Open -> Cancelled; fee-free refund to the creator's claimable balance ExpireGrant (ANYONE) : Open -> Expired once height >= deadline + ExpiryGraceBlocks; fee-free refund to the creator — the permissionless valve that makes fund-trapping impossible Claim / ClaimAll (anyone) : pays out the caller's own ledger balance (winners and refunds) WithdrawFees (fee recipient) : pays out the fee pot
AUTHORIZATION: every identity is derived from the crossing entrypoint's cur.Previous().Address() — no function takes a caller identity as a parameter. Applications and claimable balances are keyed by that runtime-derived address, so altering another user's application or claiming another user's grant is impossible by construction. The creator cannot apply to their own grant, and SelectWinner only accepts addresses that actually applied.
DEADLINE SEMANTICS: deadline = ChainHeight() + durationBlocks, fixed at creation (bounded by [MinDurationBlocks, MaxDurationBlocks]). The deadline gates NEW/UPDATED applications only; the creator may select or cancel at any time while the grant is Open. After deadline + ExpiryGraceBlocks anyone may expire the grant.
FEE MODEL: fee = floor(amount * bps / 10000), rounding favors the winner; no minimum fee; bps snapshotted into the grant at creation, so SetFeeBps affects future grants only (closes the AWARD-time admin race), and CreateGrant takes the caller's own maxFeeBps ceiling, rejecting creation if the live fee exceeds what the creator signed for (closes the CREATION-time race — audit Y1); hard compile-time cap MaxFeeBps (10%); refunds (cancel/expire) are always fee-free; the pot is withdrawable by the fee-recipient role.
MONETARY INVARIANT (conservation): let H be ugnot held at this realm's address, G = openTotal (Σ amount over Open grants), U the ledger's claimable balances, F the fee pot, S >= 0 out-of-band surplus:
H == G + U + F + S
Every transition moves value between exactly two terms inside one transaction: CreateGrant raises H and G together (coinio.Receive is the receipt-guaranteed shape, validated live on pearl-1); SelectWinner/CancelGrant/ExpireGrant move amount from G 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() = G + U + F.
ONLY GNOT: CreateGrant rejects any envelope that is not exactly one positive ugnot coin (coinio.Receive). Foreign denominations sit in surplus.
Imports
chainchain/runtimechain/runtime/unsafegno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coiniogno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledgergno.land/p/nt/avl/v0gno.land/p/nt/markdown/sanitize/v0strconv
Constants and variables
Grant status values.
const (
StatusOpen = "open"
StatusAwarded = "awarded"
StatusCancelled = "cancelled"
StatusExpired = "expired"
)
Input bounds.
const (
MaxTitleLen = 80
MaxDescLen = 2000
MaxPitchLen = 1000
// MinDurationBlocks / MaxDurationBlocks bound the application
// window a creator may configure (~5s blocks: 1 block to ~580 days).
MinDurationBlocks = int64(1)
MaxDurationBlocks = int64(10_000_000)
// ExpiryGraceBlocks after the deadline, an Open grant becomes
// expirable by anyone (~8 minutes at 5s blocks — short because
// this is a testnet deployment; a production fork would raise it).
ExpiryGraceBlocks = int64(100)
)
Denom is the only asset this realm accepts.
const Denom = "ugnot"
MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%.
const MaxFeeBps = int64(1000)
Functions
ApplicationOf
func ApplicationOf(id int64, addr address) (pitch string, height int64, ok bool)
ApplicationOf returns addr's pitch and submission height for a grant, with ok reporting whether an application exists.
Apply
func Apply(cur realm, id int64, pitch string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asApply(cross(cur), ...).
Apply submits (or re-submits) the caller's application to an open grant before its deadline. One application per address per grant — re-applying replaces the caller's own pitch only. The creator cannot apply to their own grant.
BalanceOf
func BalanceOf(addr address) int64
BalanceOf returns addr's claimable balance (won grants + refunds).
CancelGrant
func CancelGrant(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asCancelGrant(cross(cur), ...).
CancelGrant closes an open grant and refunds its escrow to the creator's claimable balance, fee-free. Only the creator may cancel. Terminal.
Claim
func Claim(cur realm, amount int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asClaim(cross(cur), ...).
Claim sends amount ugnot of the caller's claimable balance (won grants and refunds) back to the caller.
ClaimAll
func ClaimAll(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asClaimAll(cross(cur), ...).
ClaimAll sends the caller's entire claimable balance back to the caller. Fails if there is nothing to claim.
CreateGrant
func CreateGrant(cur realm, title, description string, durationBlocks, maxFeeBps int64) int64
Crossing function. Callable from a transaction via
MsgCall, and from another realm asCreateGrant(cross(cur), ...).
CreateGrant escrows the attached GNOT as a new open grant and returns its id. Only direct EOA calls with -send are accepted. The current protocol fee is snapshotted into the grant — and must not exceed maxFeeBps, the ceiling the caller signed for (rejects a fee raise sequenced ahead of this transaction); pass MaxFeeBps to accept any legal fee. The application window is durationBlocks from now.
Description
func Description(id int64) string
Description returns a grant's raw description text.
ExpireGrant
func ExpireGrant(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asExpireGrant(cross(cur), ...).
ExpireGrant closes an open grant whose deadline passed more than ExpiryGraceBlocks ago, refunding the creator fee-free. ANYONE may call it — this is the permissionless valve that guarantees escrow can never be trapped by an inactive creator. Terminal.
FeeBps
func FeeBps() int64
FeeBps returns the fee that will be snapshotted into newly created grants (existing grants keep their own snapshot).
FeeOn
func FeeOn(amount int64) (fee, credited int64)
FeeOn previews the fee and net payout for a grant of amount at the CURRENT FeeBps.
FeesAccrued
func FeesAccrued() int64
FeesAccrued returns the fee pot (the F term).
GrantInfo
func GrantInfo(id int64) (creator address, title string, amount, feeBps, deadline int64, status string, winner address, numApplicants int64)
GrantInfo returns a grant's fields by value: creator, title, amount, snapshotted fee bps, application deadline (block height), status, winner (zero unless awarded), and number of applicants.
Height
func Height() int64
Height returns the current chain height (deadline arithmetic aid).
Held
func Held() int64
Held returns the ugnot actually held at the realm address (H).
Liabilities
func Liabilities() int64
Liabilities returns everything this realm owes: openTotal + UsersTotal + FeesAccrued.
NumGrants
func NumGrants() int64
NumGrants returns how many grants have ever been created.
OpenTotal
func OpenTotal() int64
OpenTotal returns the escrow held by open grants (the G term).
Render
func Render(path string) string
Render shows the market at "" and a grant detail at "<id>". Free text (titles are charset-restricted; descriptions are not) passes through the ecosystem sanitizer before hitting markdown.
SelectWinner
func SelectWinner(cur realm, id int64, winner address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSelectWinner(cross(cur), ...).
SelectWinner awards an open grant to one of its applicants: the escrow leaves the open pool and is credited to the winner's claimable balance through the ledger, charging the fee snapshotted at creation. Only the grant's creator may select, and only an address that actually applied can win. Terminal.
SetFeeBps
func SetFeeBps(cur realm, bps int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSetFeeBps(cross(cur), ...).
SetFeeBps sets the protocol fee snapshotted into FUTURE grants at creation. Existing grants keep the fee they were created under. Admin only; bounded by [0, MaxFeeBps].
SetFeeRecipient
func SetFeeRecipient(cur realm, next address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSetFeeRecipient(cross(cur), ...).
SetFeeRecipient re-points the fee/surplus role, including the pot accrued so far. Admin only; zero address rejected.
Surplus
func Surplus() int64
Surplus returns Held() - Liabilities() (the S term).
SweepDenom
func SweepDenom(cur realm, denom string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSweepDenom(cross(cur), ...).
SweepDenom sends the surplus of a single denomination to the fee recipient. For ugnot only the excess over Liabilities() moves; other denoms move wholly. Only the fee recipient may call it.
TransferAdmin
func TransferAdmin(cur realm, next address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asTransferAdmin(cross(cur), ...).
TransferAdmin hands the admin role to next. Admin only; zero address rejected. One-step (documented trade-off).
UsersTotal
func UsersTotal() int64
UsersTotal returns the sum of all claimable balances (the U term).
WithdrawFees
func WithdrawFees(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asWithdrawFees(cross(cur), ...).
WithdrawFees sends the accrued fee pot to the fee recipient. Only the fee recipient may call it.
Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.
Dependencies
chain-attested| Imports | chain, chain/runtime, chain/runtime/unsafe, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger, gno.land/p/nt/avl/v0, gno.land/p/nt/markdown/sanitize/v0, strconv |
|---|---|
| First-party dependencies | gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger |
| Used by | none |
Known limitations
curatedThe manifest records no limitation for this package. That is an absence of a recorded caveat, not a proof that none exists — the deployment record below is the fuller account, and it always carries its own "what was NOT verified live" section.
Source and records
| Source file | pearl/r/grants/grants.gno at commit 6a510c665a53 in the project repository (not public — the digest command above is the check that needs no repository) |
|---|---|
| Matches the deployed bytes | yes — byte-identical |
| Records | catalog/primitives.md#pre-pipeline-realmspearl/DEPLOYMENT.md |