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 pure

Package grants is a grant board governed by a member set: anyone may ask the board for money, the members vote in the...

Readme View source

grants

A grant program governed by a member set: anyone may ask the board for money, the members vote in the open, and the money leaves in tranches that each have to be earned by producing a proof the members accept.

The package is pure. It holds no coins, calls no banker, reads no chain state, and imports nothing from chain: the caller supplies the acting address, the block height and the current treasury balance, and the caller executes the transfer. That is what makes the whole state machine unit-testable without a node, and what makes the realm on top thin enough to read in one screen.

Live: r/moul/grant/v0, a personal board. A multi-member program is another realm of the same size over this same package.

Three layers

Type What it owns
Board members, requests, ballots, proofs. Every rule and every tally.
Program a Board plus the money around it: donations in, payments out, the denom.
Renderer the three markdown pages, configured by a struct literal.

A realm holds one Program, builds a Renderer inside Render, and does nothing else but turn callers into addresses and execute the Payment it is handed back.

The lifecycle

Submit ──▶ Pending ──vote──▶ Approved ──┬─▶ SubmitProof ──▶ Review ──┬─▶ Released ─┐
             │                          │                            │             │
             │                          │                            └─▶ Refused ──┘
             │                          │                                (try again)
             ├──vote──▶ Rejected        └─▶ every milestone released ──▶ Completed
             └──Withdraw──▶ Withdrawn

A Request is a title, a body, and an ordered list of Milestones, each with its own amount. Approving a request pays nothing: it only makes the first milestone claimable. To get a tranche the applicant submits a Proof (a URL, a hash, or plain text) and the members review that specific proof. A refused proof does not kill the grant, it closes one Attempt; the applicant submits another. Every attempt, accepted or not, stays on the record with the ballots that decided it.

Who may vote, and how many it takes

Every member, once, per decision. There is no changing your mind: that is the price of every ballot being a permanent public statement, stored with its voter, its reason and the height it was cast at.

The party a decision is about is excluded. An applicant does not vote on their own grant, and the subject of a membership change does not vote on their own membership. The bar is a majority of the addresses actually eligible, recomputed on every ballot, so a member who applies shrinks the room rather than packing it, and a board that grows mid-vote raises its own bar.

A one-member board is a legitimate configuration, and its majority is one: that is what a personal program looks like. It still cannot self-grant, because a sole member applying leaves zero eligible voters and Majority returns an unreachable 1 rather than 0.

Standing counts only ballots from addresses that are members right now; decisions use it. Request.Tally counts the raw record. The two differ exactly when a voter has since been removed from the board: their ballot stays readable and stops carrying weight.

Asking for someone else

SubmitFor(applicant, beneficiary, reason, …) files a request whose tranches pay an address other than the one that filed it. Submit is the same call with the two addresses equal.

The split is not a convenience. An account with nothing in it cannot pay the gas to ask for its first coins, so on a board whose purpose is to fund empty accounts, someone else filing is the only path that works. Two rules follow from that, and both are in the library:

  • The reason is required when the payee is someone else. Nothing verifies it; it is a claim by the applicant, and the renderer prints it under its own heading so a member can check it before voting.
  • Both addresses are excluded from voting, on the request and on every proof. A member paid by a request a friend filed is the same conflict of interest with one address in between. Request.Excludes is the predicate, Board.Eligible recounts over it, and the majority moves with it.

Either the applicant or the beneficiary may SubmitProof: the payee may not be able to transact until the first tranche lands, and once they can, they have to be able to show their own work. Request.Payee() is who a released tranche pays, and it is what the Payment handed back to the realm carries.

Membership is a request like any other

SubmitMemberChange files a KindMember request. It asks for no money, and when it carries it executes immediately, going straight to Completed. Only a member may file one: opening a grant board's own composition to anyone with a keypair is how it gets captured. The last member cannot be removed.

The money: Program

 1p := grants.NewProgram("ugnot", alice, bob, carol)
 2
 3p.Fund(donor, 5000, height)                 // put a name on a transfer that already happened
 4r, _ := p.Apply(dave, "Port the thing", "why it matters", "design:100,ship:400", height)
 5
 6p.Board.Vote(alice, r.ID, true, "cheap for what it tells us", height)
 7p.Board.Vote(bob, r.ID, true, "agreed", height)          // majority of three: approved
 8
 9p.Board.SubmitProof(dave, r.ID, 0, grants.Proof{Kind: "url", Ref: "https://…", Height: height})
10p.Review(alice, r.ID, 0, true, "merged, I reviewed it", height, balance)
11out, pay, err := p.Review(carol, r.ID, 0, true, "confirmed", height, balance)
12if pay != nil {
13    // out == grants.Accepted. Send pay.Amount to pay.To; it is already on the ledger.
14}

The treasury is not escrowed. Committed() is what approved-but-unreleased milestones add up to, Available(balance) is the balance minus that, and it can go negative. That is deliberate: a board that can only approve what it already holds cannot approve anything before a donor shows up.

The cost is that a release can come due against an empty treasury, so Program.Review takes the balance and checks, before recording anything, whether this verdict would release a tranche it cannot cover. If so it returns ErrUnderfunded and changes nothing: no ballot, no release, no payment. A refusal is always free to record, because it costs the treasury nothing. Board.Decides and Board.ReviewDecides expose the same preview for anything else that needs to check a precondition it cannot roll back.

The pages: Renderer

Renderer serves the board at "", one request at request/<id>, and the money trail at ledger. Everything program-specific is a field (Title, Intro, Notes, Path, Link, Treasury, Balance, Footer, and a Note func(*Request) string for per-request callouts), so a realm's whole Render is a struct literal.

Build it inside Render, not in a realm global: Note is a func, and Balance has to be read fresh on every call anyway. The five ExampleRenderer* tests pin every page of a board mid-flight, so a change to any rule shows up as a diff in the markdown.

Escaping happens here, once. Everything a caller typed goes through p/moul/kit/ui and p/nt/markdown/sanitize on its way to the page, so a realm neither repeats it nor pre-escapes (escaping twice shows the backslashes). The bar differs by slot on purpose: a one-line slot (a title, a milestone name, a reason on a ballot) keeps nothing a caller typed as markup, while a prose slot (the body of an application, the note on a proof) goes through sanitize.Block, which preserves inline links and emphasis because a grant application whose link to the merged PR renders as literal text is a worse page. What Block still kills is everything structural, so a paragraph cannot leave its paragraph. The realm's own Title, Intro, Notes and Footer are chrome, written by whoever deployed it, and are emitted as-is.

TestEveryCallerSuppliedStringIsEscaped drives one board through every caller-controlled slot and asserts no structural line of any page carries a live link, an image or a gnoweb tag. It asserts the dangerous sequence is dead, never the exact escaped bytes: those belong to the sanitizer and change when it changes.

What this deliberately does not do

No weights, no delegation, no quadratic anything, no deadline. A request with no majority either way stays Pending until someone breaks the tie or the applicant withdraws it. Those are all reasonable things to build on top; none is needed to show the shape.


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/p/moul/grants/v0 dependency graph

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

Overview

Package grants is a grant board governed by a member set: anyone may ask the board for money, the members vote in the open, and the money leaves in tranches that each have to be earned by producing a proof the members accept.

The package is pure. It holds no coins, reads no chain state and imports nothing from `chain`: the caller supplies the acting address and the block height, and the caller performs the transfer. That is what makes the whole state machine unit-testable without a node, and it is what lets the same board be driven by a realm, by a test, or by a different payment rail. Live board: gno.land/r/moul/grant/v0.

The shape of a grant

A Request is a title, a body, and an ordered list of Milestones, each with its own amount. Approving a request does not pay anything: it only makes the first milestone claimable. To get a tranche the applicant submits a Proof (a URL, a hash, or plain text) and the members review that specific proof. A rejected proof does not kill the grant, it just closes one Attempt; the applicant submits another. Every attempt, accepted or not, stays on the record with the ballots that decided it.

Asking for someone else

A request has an applicant, who files it and answers for it, and a beneficiary, who gets paid. SubmitFor separates the two and takes a reason for the detour; Submit is the common case where they are the same address.

The split is not a convenience. An account with nothing in it cannot pay the gas to ask for its first coins, so on a board whose whole purpose is to fund empty accounts, "someone else files it" is the ONLY path that works. Both addresses are then barred from voting on the request, and either of them may submit a proof, for the same reason: the payee may not be able to transact at all until the first tranche lands.

Who may vote

Every member, once, per decision. The party the decision is about is excluded: the applicant does not vote on their own grant, and the subject of a membership change does not vote on their own membership. The bar is a majority of the addresses that are actually eligible, recomputed on each ballot, so a board that grows mid-vote raises its own bar.

What this deliberately does not do

No weights, no delegation, no quadratic anything, no deadline. A request with no majority either way simply stays Pending until someone breaks the tie or the applicant withdraws it. Those are all reasonable things to add on top; none of them is needed to show the shape.

Constants 4

const KindGrant, KindMember

1const (
2	KindGrant  Kind = iota // pays out, milestone by milestone
3	KindMember             // adds or removes a board member, immediately
4)
source

const UnderReview, Accepted, Refused

1const (
2	UnderReview Outcome = iota // members are still voting on this proof
3	Accepted                   // the tranche is earned
4	Refused                    // this proof did not convince; another may
5)
source

const Pending, Approved, Rejected, Completed, Withdrawn

1const (
2	Pending   Status = iota // open for member ballots
3	Approved                // carried; milestones can be earned
4	Rejected                // a majority voted no
5	Completed               // every milestone released
6	Withdrawn               // the applicant pulled it before a decision
7)
source

Variables 1

var ErrNotMember, ErrNotApplicant, ErrNoRequest, ErrNoMilestone, ErrNotPending, ErrNotApproved, ErrAlreadyVoted, ErrConflict, ErrOutOfOrder, ErrProofPending, ErrNoProof, ErrBadTitle, ErrBadBody, ErrBadMilestones, ErrBadAmount, ErrBadProof, ErrBadReason, ErrNeedReason, ErrBadAddress, ErrIsMember, ErrLastMember, ErrNothingSent, ErrUnderfunded, ErrBadSpec

 1var (
 2	ErrNotMember     = errors.New("grants: not a board member")
 3	ErrNotApplicant  = errors.New("grants: only the applicant may do this")
 4	ErrNoRequest     = errors.New("grants: no such request")
 5	ErrNoMilestone   = errors.New("grants: no such milestone")
 6	ErrNotPending    = errors.New("grants: the request is not pending")
 7	ErrNotApproved   = errors.New("grants: the request is not approved")
 8	ErrAlreadyVoted  = errors.New("grants: this member already voted")
 9	ErrConflict      = errors.New("grants: the party a decision is about may not vote on it")
10	ErrOutOfOrder    = errors.New("grants: milestones are claimed in order")
11	ErrProofPending  = errors.New("grants: a proof is already under review")
12	ErrNoProof       = errors.New("grants: no proof is under review")
13	ErrBadTitle      = errors.New("grants: title must be 1 to 100 characters")
14	ErrBadBody       = errors.New("grants: body must be at most 2000 characters")
15	ErrBadMilestones = errors.New("grants: a grant needs 1 to 10 milestones")
16	ErrBadAmount     = errors.New("grants: every milestone must ask for a positive amount")
17	ErrBadProof      = errors.New("grants: proof kind must be url, hash or text, with a reference")
18	ErrBadReason     = errors.New("grants: reason must be at most 300 characters")
19	ErrNeedReason    = errors.New("grants: filing for someone else needs a reason")
20	ErrBadAddress    = errors.New("grants: invalid address")
21	ErrIsMember      = errors.New("grants: already a board member")
22	ErrLastMember    = errors.New("grants: the last member cannot be removed")
23	ErrNothingSent   = errors.New("grants: a donation must be a positive amount")
24	ErrUnderfunded   = errors.New("grants: the treasury cannot cover this tranche")
25	ErrBadSpec       = errors.New("grants: milestones must read \"title:amount,title:amount\"")
26)
source

Errors returned by the mutators. They are sentinels so a realm can map them onto its own messages, or simply panic with them.

Functions 4

func New

1func New(founders ...address) *Board
source

New returns a board whose founding members are the given addresses, all recorded as having joined at height 0. Duplicates and invalid addresses are skipped; a board with no members accepts no decisions until one is added, so callers normally pass at least one.

func NewMilestone

1func NewMilestone(title string, amount int64) *Milestone
source

NewMilestone is a small constructor so callers do not build the struct (and its Attempts slice) by hand.

func ParseMilestones

1func ParseMilestones(spec string) ([]*Milestone, error)
source

ParseMilestones reads "design:100,ship:400" into milestones, amounts in the program's denomination, paid in the order written. A title may not contain a comma, and the amount is whatever follows the LAST colon, so "port gno:land tooling:250" parses the way it reads.

func NewProgram

1func NewProgram(denom string, founders ...address) *Program
source

NewProgram returns a program whose board is seated with founders. denom is the coin everything is counted in ("ugnot" on gno.land); it is carried so a renderer can label amounts without the realm telling it twice.

Types 13

type Attempt

struct
1type Attempt struct {
2	Proof    Proof
3	Reviews  []Ballot
4	Outcome  Outcome
5	ClosedAt int64
6}
source

Attempt is one proof and the review it got. A milestone keeps every attempt, so a refused proof and the reasons it was refused stay readable forever.

type Ballot

struct
1type Ballot struct {
2	Voter   address
3	Approve bool
4	Reason  string
5	Height  int64
6}
source

Ballot is one member's vote on one decision, with the reason they gave. Reasons are not optional decoration: they are the only part of a vote that tells an applicant what to fix.

type Board

struct
1type Board struct {
2	members  *avl.Tree // address string -> int64 join height
3	requests *avl.Tree // zero-padded id -> *Request
4	nextID   int
5}
source

Board is the whole state of one grant program: who decides, and what has been asked of them.

Methods on Board

func Committed

method on Board
1func (b *Board) Committed() int64
source

Committed is what the treasury still owes on approved grants. A realm that keeps its balance at or above this number can always pay a released tranche.

func Decides

method on Board
1func (b *Board) Decides(id int, voter address, approve bool) (bool, Status)
source

Decides previews what a ballot would do without casting it, so a caller can check a precondition it cannot roll back: a realm, for instance, refusing to let a grant carry that its treasury cannot cover. It reports false for a ballot that would be refused anyway.

func Disbursed

method on Board
1func (b *Board) Disbursed() int64
source

Disbursed is everything the board has ever released.

func Eligible

method on Board
1func (b *Board) Eligible(r *Request) int
source

Eligible is how many members may vote on a request: everyone but the parties the decision is about.

func Get

method on Board
1func (b *Board) Get(id int) *Request
source

Get returns a request by id, or nil.

func IsMember

method on Board
1func (b *Board) IsMember(addr address) bool
source

IsMember reports whether addr sits on the board.

func JoinedAt

method on Board
1func (b *Board) JoinedAt(addr address) int64
source

JoinedAt returns the height a member joined, or -1 for a non-member.

func List

method on Board
1func (b *Board) List() []*Request
source

List returns every request in id order.

func Majority

method on Board
1func (b *Board) Majority(r *Request) int
source

Majority is the number of concurring ballots a decision on r needs. It is recomputed on every ballot, so a board that changes size mid-vote moves its own bar rather than freezing a stale one.

func MemberCount

method on Board
1func (b *Board) MemberCount() int
source

MemberCount is the number of addresses on the board.

func Members

method on Board
1func (b *Board) Members() []address
source

Members lists the board in address order, which is stable across calls.

func Review

method on Board
1func (b *Board) Review(voter address, id, idx int, accept bool, reason string, height int64) (Outcome, error)
source

Review casts a member's verdict on the proof currently under review and returns the outcome that verdict left the attempt in. Accepting it releases the tranche, which in this package means marking it Released and nothing more; moving the coins is the caller's job, and Request.Paid tells it how much it now owes. Refusing closes the attempt and lets the applicant try again with better evidence.

func ReviewDecides

method on Board
1func (b *Board) ReviewDecides(id, idx int, voter address, accept bool) (bool, Outcome)
source

ReviewDecides is Decides for a proof review: it reports whether this verdict would close the attempt under review, and how. A realm uses it to check, before recording anything, that it can actually pay a tranche it is about to release.

func Size

method on Board
1func (b *Board) Size() int
source

Size is the number of requests ever filed.

func Standing

method on Board
1func (b *Board) Standing(r *Request) (yes, no int)
source

Standing counts only the ballots of addresses that are members right now. Decisions use this, not Request.Tally: a ballot is a permanent record of what someone said, but it stops carrying weight the moment they leave the board. The two numbers differ exactly when a voter has since been removed, and a good renderer shows both.

func Submit

method on Board
1func (b *Board) Submit(applicant address, title, body string, ms []*Milestone, height int64) (*Request, error)
source

Submit files a grant application for the applicant themselves. Anyone may apply; being a member is not required and does not help, since a member is barred from voting on their own request.

func SubmitFor

method on Board
1func (b *Board) SubmitFor(applicant, beneficiary address, reason, title, body string, ms []*Milestone, height int64) (*Request, error)
source

SubmitFor files a grant application whose money goes to beneficiary rather than to the applicant, with a reason for the detour.

The reason is required, and it is required because it is the only thing on the page that answers the question a reader will have: why is this person asking for someone else's money. The usual honest answer is that the payee has an empty account and cannot pay the gas to ask, which is exactly the case a grant board exists to serve and exactly the case an impersonation looks like. Making the claim explicit is what lets a member check it.

Passing the applicant's own address is the same as Submit and needs no reason.

func SubmitMemberChange

method on Board
1func (b *Board) SubmitMemberChange(proposer, subject address, add bool, body string, height int64) (*Request, error)
source

SubmitMemberChange files a request to add or remove a board member. Only a member may file one: opening the board's own composition to anyone with a keypair is how a grant board gets captured.

func SubmitProof

method on Board
1func (b *Board) SubmitProof(caller address, id, idx int, p Proof) error
source

SubmitProof offers evidence for the next unreleased milestone of an approved grant. Milestones are earned in order, and only one proof is under review at a time.

Either the applicant or the beneficiary may submit. On a request filed for an empty account, the payee cannot transact until the first tranche lands, so restricting this to the payee would strand the grant it was filed to unstick; restricting it to the filer would leave the payee unable to show their own work once they can.

func Vote

method on Board
1func (b *Board) Vote(voter address, id int, approve bool, reason string, height int64) (Status, error)
source

Vote casts one member's ballot on a request and returns the status it left the request in. A member votes once; there is no changing your mind, which is the price of every ballot being a permanent public statement.

func Withdraw

method on Board
1func (b *Board) Withdraw(caller address, id int, height int64) error
source

Withdraw lets an applicant pull their own request before it is decided.

type Donation

struct
1type Donation struct {
2	Height int64
3	From   address
4	Amount int64
5}
source

Donation is one top-up of the treasury, credited to a name.

type Kind

ident
1type Kind int
source

Kind says what carrying a request actually does.

Methods on Kind

func String

method on Kind
1func (k Kind) String() string
source

type Milestone

struct
1type Milestone struct {
2	Title      string
3	Amount     int64 // in the smallest unit of whatever the realm pays in
4	Attempts   []*Attempt
5	Released   bool
6	ReleasedAt int64
7}
source

Milestone is one tranche of a grant: what has to be shown, and what it pays.

Methods on Milestone

func Current

method on Milestone
1func (m *Milestone) Current() *Attempt
source

Current returns the attempt still under review, or nil.

type Outcome

ident
1type Outcome int
source

Outcome is the result of one proof attempt.

Methods on Outcome

func String

method on Outcome
1func (o Outcome) String() string
source

type Payment

struct
1type Payment struct {
2	Height    int64
3	Request   int
4	Milestone int
5	To        address
6	Amount    int64
7}
source

Payment is one released tranche. The ledger of these is a program's answer to "where did the money go".

type Program

struct
1type Program struct {
2	Board     *Board
3	Denom     string
4	Donations []Donation
5	Payments  []Payment
6}
source

Program is a Board plus the money that moves around it: what came in, what went out, and the denomination it is all counted in.

It still holds no coins and calls no banker. A realm hands it the current treasury balance whenever a decision depends on one, and executes the Payment it hands back. That split is the whole point: everything a grant program decides is testable without a node, and the realm on top is thin enough to read in one screen.

Methods on Program

func Apply

method on Program
1func (p *Program) Apply(applicant address, title, body, spec string, height int64) (*Request, error)
source

Apply files a grant request from a milestone spec, "design:100,ship:400". See ParseMilestones for the grammar.

func ApplyFor

method on Program
1func (p *Program) ApplyFor(applicant, beneficiary address, reason, title, body, spec string, height int64) (*Request, error)
source

ApplyFor is Apply for a request whose money goes to someone other than the address filing it. See Board.SubmitFor for why the reason is required.

func Available

method on Program
1func (p *Program) Available(balance int64) int64
source

Available is balance minus what is already promised. It goes NEGATIVE when the board has approved more than the treasury holds, which is allowed on purpose: a board that can only approve what it already has cannot approve anything before a donor shows up. Renderers say so out loud.

func Committed

method on Program
1func (p *Program) Committed() int64
source

Committed is what approved grants can still claim.

func Disbursed

method on Program
1func (p *Program) Disbursed() int64
source

Disbursed is everything ever released.

func Fund

method on Program
1func (p *Program) Fund(from address, amount, height int64) error
source

Fund records a top-up. The coins have already moved by the time this is called: a realm calls it to put a name next to a transfer that would otherwise be anonymous.

func Raised

method on Program
1func (p *Program) Raised() int64
source

Raised is everything ever donated through Fund.

func Review

method on Program
1func (p *Program) Review(voter address, id, idx int, accept bool, reason string, height, balance int64) (Outcome, *Payment, error)
source

Review casts a member's verdict on the proof under review and, when that verdict releases the tranche, records the payment and hands it back for the caller to execute.

balance is what the treasury holds right now. A verdict that would release more than that is refused with ErrUnderfunded and changes NOTHING: no ballot, no release, no payment. That ordering is the reason Board.Review is not called directly by a realm. A refusal is always free to record, since it costs the treasury nothing.

type Proof

struct
1type Proof struct {
2	Kind   string
3	Ref    string
4	Note   string
5	Height int64
6}
source

Proof is the evidence an applicant offers for one milestone. Kind is "url", "hash" or "text"; the board decides what it is worth.

type Renderer

struct
 1type Renderer struct {
 2	Program  *Program
 3	Title    string   // page heading
 4	Intro    string   // markdown under the heading, already wrapped
 5	Notes    []string // extra paragraphs on the board page: house rules, scope
 6	Path     string   // package path, for the gnokey examples
 7	Link     string   // gnoweb route prefix, e.g. "/r/moul/grant/v0"
 8	Treasury address  // where the money sits
 9	Balance  int64    // what it holds right now
10	Footer   string   // closing paragraph on the board page
11
12	// Note returns an extra callout for one request page, or "". Use it for
13	// anything the library cannot know, such as a seeded request whose
14	// applicant address has no key behind it.
15	Note func(*Request) string
16}
source

Renderer turns a Program into the three markdown pages a grant program needs: the board, one request, and the ledger. Everything that differs between one program and the next is a field, so a realm's whole Render is building this literal and calling it.

Build it INSIDE Render rather than storing it in a realm global: Note is a func, and Balance has to be read fresh on every call anyway.

Escaping

Everything a caller typed (titles, bodies, reasons, proofs, notes) is escaped here, once, through p/moul/kit/ui and p/nt/markdown/sanitize. A realm does not repeat it and must not pre-escape: escaping twice shows the backslashes. The realm's OWN fields below (Title, Intro, Notes, Footer) are chrome, are written by whoever deployed the realm, and are emitted as-is so they can carry markdown.

Methods on Renderer

func Render

method on Renderer
1func (rr Renderer) Render(path string) string
source

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

type Request

struct
 1type Request struct {
 2	ID          int
 3	Kind        Kind
 4	Applicant   address // who filed it and answers for it
 5	Beneficiary address // who gets paid; equal to Applicant on a self request
 6	Reason      string  // why the applicant is asking on someone else's behalf
 7	Title       string
 8	Body        string
 9	Milestones  []*Milestone
10	Subject     address // KindMember only: the address being added or removed
11	Add         bool    // KindMember only: true adds, false removes
12	Votes       []Ballot
13	Status      Status
14	CreatedAt   int64
15	DecidedAt   int64
16}
source

Request is a grant application or a membership change.

Methods on Request

func BallotOf

method on Request
1func (r *Request) BallotOf(voter address) *Ballot
source

BallotOf returns the member's ballot on the request, or nil.

func Excludes

method on Request
1func (r *Request) Excludes(addr address) bool
source

Excludes reports whether addr is barred from voting on this request because the decision is about them.

A grant excludes BOTH parties, not just the one who typed the transaction: letting a member be paid by a request a friend filed for them is the same conflict with one more step in it.

func Next

method on Request
1func (r *Request) Next() int
source

Next is the index of the first unreleased milestone, or -1 when the grant is fully paid. Milestones are earned in order.

func OnBehalf

method on Request
1func (r *Request) OnBehalf() bool
source

OnBehalf reports whether this grant was filed by one address for another.

func Outstanding

method on Request
1func (r *Request) Outstanding() int64
source

Outstanding is what an approved request can still cost the treasury. It is zero for anything not approved, which is what makes it safe to sum across the board as the committed amount.

func Paid

method on Request
1func (r *Request) Paid() int64
source

Paid is what the milestones released so far add up to.

func Payee

method on Request
1func (r *Request) Payee() address
source

Payee is the address a released tranche pays. It is the beneficiary, which is the applicant unless the request was filed for someone else.

func Tally

method on Request
1func (r *Request) Tally() (yes, no int)
source

Tally counts the ballots cast on the request itself.

func Total

method on Request
1func (r *Request) Total() int64
source

Total is the sum of every milestone, released or not.

type Status

ident
1type Status int
source

Status is where a request sits in its lifecycle.

Methods on Status

func String

method on Status
1func (s Status) String() string
source

Imports 6

Source Files 9