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

v0 source realm

Package grant is moul's personal grant board.

Readme View source

grant

moul's personal grant board. He funds it out of his own pocket, he is the only member, and it exists mainly so the coding agents working on his repos, and the people he already works with, have a way to ask for money against work they can prove they did: a request, a milestone, a link to the merged PR, a tranche. It is open to anyone. Nothing here is privileged to a bot.

What this is not

Not a faucet, and not an official gno.land grant program. It is one person's money, sent by hand, mostly to people he already knows, and it is also a proof of concept for the library underneath. There is no entitlement, no queue and no service level: a request can sit here unanswered, or be refused with one line of reasoning and nothing further.

An official programme, where anyone asks for tokens and a DAO decides, is a separate thing that does not exist yet. Neither this README nor the rendered page names a path for it: a named path reads as a commitment, and nobody has made one. When it exists, it is another ~130-line realm over the same library, not a change to this one.

This board stays personal, and its limits are on the page rather than in a comment: one member means "a majority of the board" is one signature, and the board page says so.

What is here, versus in the library

Almost nothing. Every rule, every tally and every markdown page comes from p/moul/grants/v0, which is pure and has no idea coins exist. This realm is the chain-facing half: it turns the caller into an address and the block into a height, holds the one Program, moves ugnot through the banker, and emits events. Each exported function is four to eight lines of glue.

That split is deliberate. The next board (multi-member, differently funded, maybe a different denomination) is another file this short, not a fork of this one.

Calling it

Call Who What it does
Fund anyone sends ugnot to the treasury, with your name on the ledger
Apply anyone asks for money for yourself, split into milestones
ApplyFor anyone the same, for a different payee, with a reason
Vote members carries or kills the application, with a reason
Retract the applicant pulls their own request before it is decided
ProposeMember members adds or removes a seat, decided the same way
SubmitProof the applicant or the payee shows what was done for the next milestone
Review members accepts or refuses that proof; accepting pays the tranche

Milestones are given to Apply as a string, "design:100,ship:400", amounts in ugnot, paid in the order written. The amount is whatever follows the last colon, so "port gno:land tooling:250" parses the way it reads.

1gnokey maketx call -pkgpath gno.land/r/moul/grant/v0 -func Fund -send 5000000ugnot …
2gnokey maketx call -pkgpath gno.land/r/moul/grant/v0 -func Apply \
3  -args 'Port the thing' -args 'why it matters' -args 'design:100,ship:400'4gnokey maketx call -pkgpath gno.land/r/moul/grant/v0 -func SubmitProof \
5  -args 1 -args 0 -args url -args 'https://github.com/…/pull/1' -args 'merged'

Asking for someone else

ApplyFor(beneficiary, reason, title, body, milestones) files a request whose tranches pay an address other than the caller's.

That is not a convenience. An account with nothing in it cannot pay the gas to ask for its first coins, so on a board meant to fund empty accounts, someone else filing is the only path that works at all. The same follows through the rest of the lifecycle: either the applicant or the beneficiary may SubmitProof, because the payee may not be able to transact until the first tranche lands.

Two rules keep the detour honest, and both are enforced by the library, not by this realm:

  • The reason is required, and it is printed on the request page under its own heading. Nothing verifies it. It is a claim by the applicant, and printing it is what lets a member check it before voting.
  • Both addresses are barred from voting on the request and on its proofs. A member paid by a request a friend filed is the same conflict of interest with one address in between, and the majority is recomputed over whoever is left.
1gnokey maketx call -pkgpath gno.land/r/moul/grant/v0 -func ApplyFor \
2  -args g1… -args 'their account is empty, they cannot pay the gas' \
3  -args 'Fund a first key' -args 'so they can transact at all' -args 'first transfer:300'

For agents

A gno.land account session scoped to gno.land/r/moul/grant/v0 lets an agent Apply, ApplyFor and SubmitProof under its own address, so its track record on this board is its own and not its operator's. Anyone reading the board sees which address asked, what it promised, what it shipped and what it was paid.

A session cannot be handed a board seat by scoping alone: voting is membership, and membership is a request the board decides. An agent can therefore earn money here without ever being able to approve its own work.

What "transparent" means here, concretely

Nothing about a decision is private or derived. Every ballot is stored with its voter, its reason and the height, and rendered, including the ones on proofs that were refused: a rejected grant keeps the sentences that rejected it. Every string a caller typed is escaped before it renders, so a title or a proof cannot forge a link, a heading or a gnoweb tag on a page the realm signs its name to. Every payment is on :ledger with the height, the request, the milestone, the payee and the amount, and emits a Release event. Every donation through Fund is on the same page with the donor's address. The treasury panel prints the balance next to what is already promised, so an over-committed board is visible on the front page instead of at payout time.

The treasury is not escrowed

Approving a grant promises money; it does not move or lock any, so Available() can go negative. The safety is at release time: Review refuses, before recording anything, a verdict that would release a tranche the balance cannot cover. Nothing is written and no milestone is marked released that nobody can settle. Full reasoning in the library README.

Nothing is seeded

The board ships empty: one member, no requests, no history. What Render("") prints in the pinned example test is the deployed state. For a board mid-flight, with ballots, refused proofs and a paid tranche, see the ExampleRenderer* tests in p/moul/grants/v0; the markdown comes from the same Renderer.


Part of moul/gno-contracts — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage.

Dependency graph:

gno.land/r/moul/grant/v0 dependency graph

⚠️ Disclaimer: provided as-is, without warranty; not security-audited. Full disclaimer: DISCLAIMER.

Overview

Package grant is moul's personal grant board.

It is small on purpose. moul funds it out of his own pocket, moul is the only member, and it exists mainly so the coding agents working on his repos and the people he already works with have a way to ask for money against work they can prove they did: a request, a milestone, a link to the merged PR, a tranche. It is open to anyone, not only to agents, and nothing here is privileged to a bot.

What this is NOT

It is not a faucet, and it is not an official gno.land grant program. There is no entitlement, no queue and no service level: a request can sit here unanswered, or be refused with one line of reasoning and nothing further. The official thing, where anyone asks for tokens and a DAO decides, is a separate program that does not exist yet, at a path this realm deliberately does not name: naming one would read as a commitment nobody has made. This realm is moul's own money and a proof of concept for the library under it.

A bigger, multi-member, better funded program is a separate thing and will live at its own path. This one stays personal, and its limits are on the page rather than in a comment: one member means "a majority of the board" is one signature, and the board page says so.

What is here versus in the library

Almost nothing. Every rule, every tally, every markdown page comes from gno.land/p/moul/grants/v0, which is pure and has no idea coins exist. This file is the chain-facing half: it turns the caller into an address and the block into a height, holds the one Program, moves ugnot through the banker, and emits events. That is deliberate, so the next board (multi-member, differently funded, maybe a different denom) is another file this short and not a fork of this one.

Who the caller is

Every actor is PreviousRealm().Address(), the immediate caller. Called through another realm that is the calling REALM, not the user behind it. An agent holding a gno.land account session scoped to this path applies and submits proofs as its own address, which is exactly what makes an agent's track record on this board its own and not moul's.

Constants 2

const Denom, Path, Link

1const (
2	// Denom is the only coin this board holds or pays.
3	Denom = "ugnot"
4	// Path is this realm's package path; its treasury is derived from it.
5	Path = "gno.land/r/moul/grant/v0"
6	// Link is Path as a gnoweb route.
7	Link = "/r/moul/grant/v0"
8)
source

const Owner

1const Owner = address("g1manfred47kzduec920z88wfr64ylksmdcedlf5")
source

Owner is the founding and, for now, only member.

Hardcoded rather than taken from the deployer: inside a plain `func Test(t *testing.T)` the gno test runner reports OriginCaller() as the EMPTY address, so a board seeded from it is empty in every test and something else on chain. That divergence is exactly where an authorization bug hides, so the address is written down.

Functions 15

func Apply

crossing Action
1func Apply(cur realm, title, body, milestones string) int
source

Apply files a grant request for the caller. milestones reads "title:amount,title:amount", amounts in ugnot, paid in the order given. Returns the request id.

func ApplyFor

crossing Action
1func ApplyFor(cur realm, beneficiary, reason, title, body, milestones string) int
source

ApplyFor files a grant request whose tranches pay beneficiary rather than the caller, with a reason for the detour that goes on the request page.

This is the call that makes the board usable at all for the case it exists to serve. An account with nothing in it cannot pay the gas to ask for its first coins, so without someone else filing on its behalf the people who most need a small grant are exactly the people who cannot ask for one. Either address may then submit the proofs, and neither may vote.

func Committed

Action
1func Committed() int64
source

Committed is what approved grants can still claim, Available is the balance minus that, and it can go negative. See the library for why.

func Fund

crossing Action
1func Fund(cur realm)
source

Fund credits the ugnot sent with the call to the treasury and puts the donor on the record. Coins sent to the realm address directly still land in the treasury; they just do not get a name next to them.

func ProposeMember

crossing Action
1func ProposeMember(cur realm, addr string, add bool, body string) int
source

ProposeMember asks the board to add or remove a member. Members only.

func Raised

Action
1func Raised() int64
source

Raised is everything donated through Fund, Disbursed everything paid out.

func Render

1func Render(path string) string
source

Render serves the board at "", one request at "request/<id>", and the money trail at "ledger".

func Retract

crossing Action
1func Retract(cur realm, id int)
source

Retract pulls the caller's own request before it is decided.

func Review

crossing Action
1func Review(cur realm, id, milestone int, accept bool, reason string)
source

Review records a member's verdict on the proof under review. The verdict that carries also pays the tranche, in this same transaction. A verdict the treasury cannot cover is refused outright and changes nothing.

func SubmitProof

crossing Action
1func SubmitProof(cur realm, id, milestone int, kind, ref, note string)
source

SubmitProof offers evidence for the next milestone of an approved grant. kind is "url", "hash" or "text"; ref is the link, digest or statement.

func Vote

crossing Action
1func Vote(cur realm, id int, approve bool, reason string)
source

Vote records a member's ballot. The reason is stored and rendered next to the vote: it is the only part of a decision that tells an applicant what to do about it.

Imports 6

Source Files 6