govern.gno
5.47 Kb · 165 lines
1package sugarproxy
2
3import (
4 "strconv"
5 "strings"
6
7 governor "gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/governor/v0"
8 grc20votes "gno.land/p/g1ecsuj0q572jr0dhu29q9njtnmw03hyu7tyyvv6/grc20votes/v0"
9)
10
11// SUGAR, the governance token: kourt's grc20votes ledger (balances checkpointed per epoch of 720 blocks, about an
12// hour, so a vote weighs what a holder held before the proposal) and kourt's governor over it. Both packages are
13// deployed under Jae's address on gnoland-1.
14//
15// The governor's proposals here are of two kinds of this realm's own: add an item, remove one. Holders propose
16// with Propose(cur, kind, payload, title), vote with Vote, and anyone may Execute a proposal that passed and
17// whose delay is over.
18var (
19 ledger = grc20votes.NewLedger("sugar proxy", "SUGAR", 6, 720)
20 engine = governor.New(ledger, ledger)
21)
22
23// InitialSupply is minted to the deployer at init: 1,000,000 SUGAR of 6 decimals.
24const InitialSupply = int64(1_000_000_000_000)
25
26const (
27 kindAdd = governor.Reserved + "sugar-add"
28 kindRemove = governor.Reserved + "sugar-remove"
29)
30
31// rules are kourt's bootstrap rules (a fifth of the supply turns out, two thirds of the votes cast say yes, a week
32// to vote, two days' delay, two weeks to execute), except that proposing takes 0.1% of the supply: kourt's lets
33// anyone propose.
34func rules() governor.Rules {
35 r := governor.BootstrapRules()
36 r.ProposeBps = 10
37 return r
38}
39
40// setupGovernor adopts this realm's two kinds; init calls it.
41func setupGovernor() {
42 engine.InstallBuiltin(addKind{}, rules())
43 engine.InstallBuiltin(removeKind{}, rules())
44}
45
46func mintInitial(to address) { ledger.Mint(to, InitialSupply) }
47
48// KindAdd and KindRemove are the names to propose under.
49func KindAdd() string { return kindAdd }
50func KindRemove() string { return kindRemove }
51
52type addKind struct{}
53
54func (addKind) Name() string { return kindAdd }
55func (addKind) Describe(payload string) string {
56 return "Add to sugar proxy's rules: `" + payload + "`. Once executed it waits out the veto window for its kind, " +
57 "during which GovDAO or the guardian may veto it."
58}
59func (addKind) Check(payload string) error {
60 _, _, err := parse(payload)
61 return err
62}
63func (addKind) Do(_ int, _ realm, payload string) error {
64 _, err := add(payload, "token")
65 return err
66}
67
68type removeKind struct{}
69
70func (removeKind) Name() string { return kindRemove }
71func (removeKind) Describe(payload string) string {
72 return "Remove item " + payload + " from sugar proxy's rules, at once."
73}
74func (removeKind) Check(payload string) error {
75 id, err := strconv.Atoi(strings.TrimSpace(payload))
76 if err != nil {
77 return errorf("the payload is an item's number")
78 }
79 if it := find(id); it == nil || it.State != StateApproved {
80 return errorf("item %d isn't approved", id)
81 }
82 return nil
83}
84func (removeKind) Do(_ int, _ realm, payload string) error {
85 id, _ := strconv.Atoi(strings.TrimSpace(payload))
86 return end(id, false, "token")
87}
88
89const errStaleRealm = "sugarproxy: that realm value is not live; pass your own cur"
90
91func live(cur realm) {
92 if !cur.IsCurrent() {
93 panic(errStaleRealm)
94 }
95}
96
97// Propose opens a proposal: kind is KindAdd() with a payload "kind|field|...", or KindRemove() with an item's
98// number. It needs the governor's share of the supply to propose.
99func Propose(cur realm, kind, payload, title string) int64 {
100 live(cur)
101 return engine.Propose(cur.Previous().Address(), kind, payload, title)
102}
103
104// Vote casts the caller's weight: "yes", "no" or "abstain".
105func Vote(cur realm, id int64, choice string) {
106 live(cur)
107 engine.Vote(cur.Previous().Address(), id, choice)
108}
109
110// Execute carries out a proposal that passed, once its delay is over.
111func Execute(cur realm, id int64) {
112 live(cur)
113 engine.Execute(cur.Previous().Address(), id, func(k governor.Kind, sub, payload string) error {
114 return k.Do(0, cur.Sub(sub), payload)
115 })
116}
117
118// Cancel withdraws the caller's own open proposal.
119func Cancel(cur realm, id int64) {
120 live(cur)
121 engine.Cancel(cur.Previous().Address(), id)
122}
123
124// Settle closes a proposal whose voting is over.
125func Settle(cur realm, id int64) {
126 live(cur)
127 engine.Settle(cur.Previous().Address(), id)
128}
129
130// State, Describe and Tally read a proposal.
131func State(id int64) string { return engine.State(id) }
132func DescribeProposal(id int64) string { return engine.Describe(id) }
133func Tally(id int64) (yes, no, abstain, total int64) { return engine.Tally(id) }
134
135// --- the token ---
136
137func TotalSupply() int64 { return ledger.TotalSupply() }
138func BalanceOf(who address) int64 { return ledger.BalanceOf(who) }
139func VotesOf(who address) int64 { return ledger.VotesOf(who) }
140func DelegateOf(who address) address { return ledger.DelegateOf(who) }
141func Allowance(o, s address) int64 { return ledger.Allowance(o, s) }
142
143// Transfer sends SUGAR from the caller.
144func Transfer(cur realm, to address, amount int64) {
145 live(cur)
146 ledger.Transfer(cur.Previous().Address(), to, amount)
147}
148
149// Approve lets spender move up to amount of the caller's SUGAR.
150func Approve(cur realm, spender address, amount int64) {
151 live(cur)
152 ledger.Approve(cur.Previous().Address(), spender, amount)
153}
154
155// TransferFrom moves SUGAR the owner approved the caller to move.
156func TransferFrom(cur realm, from, to address, amount int64) {
157 live(cur)
158 ledger.TransferFrom(cur.Previous().Address(), from, to, amount)
159}
160
161// DelegateVotes gives the caller's voting weight to another address (its own, to take it back).
162func DelegateVotes(cur realm, to address) {
163 live(cur)
164 ledger.Delegate(cur.Previous().Address(), to)
165}