gno.land/r/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/bubble⧉ Realm Open in gnoweb ↗
Kind Realm (renderable)
Name bubble
Namespace g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr
Exported functions 54
Module gno.land/r/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/bubble
gno 0.9 kinds.gno gno
⧉
1 package bubble 2 3 import ( 4 "strconv" 5 "strings" 6 7 governor "gno.land/p/g1leu8d2vsplhehcfkjg50mwgdpxdkt8tztu95wr/governor/v0" 8 Admin () string
Allowance (owner string, spender string) int64
Approve (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, spender string, amount int64)
BalanceOf (owner string) int64
Batches () int64
Burn (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, amount int64)
Cancel (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64)
DayCap () int64
Delegate (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string)
DelegateOf (who string) string
Describe (id int64) string
Epoch () uint32
Execute (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64)
GetDecimals () int
GetName () string
GetSymbol () string
HasVoted (id int64, who string) bool
KindRules (name string) (struct{QuorumBps int64; ThresholdBps int64; ProposeBps int64; VotingBlocks int64; DelayBlocks int64; GraceBlocks int64}, bool)
LastBatch () string
MeanTurnout () int64
Mint (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, amount int64)
MintedToday () (minted int64, ceiling int64)
Minter () string
MintMany (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, batchID string, list string)
NewRules (quorumBps int64, thresholdBps int64, votingBlocks int64, delayBlocks int64, graceBlocks int64, proposeBps int64) struct{QuorumBps int64; ThresholdBps int64; ProposeBps int64; VotingBlocks int64; DelayBlocks int64; GraceBlocks int64}
Offer (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, k interface {Check func(string) .uverse.error; Describe func(string) string; Do func(int, .uverse.realm, string) .uverse.error; Name func() string}, r struct{QuorumBps int64; ThresholdBps int64; ProposeBps int64; VotingBlocks int64; DelayBlocks int64; GraceBlocks int64})
PastTotalVotes (at uint32) int64
PastVotes (who string, at uint32) int64
Paused () bool
Premined () int64
Preview (kind string, payload string) string
Propose (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, kind string, payload string, title string) int64
ReferralMinted () int64
ReleaseRoll (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64)
Render (path string) string
RenounceAdmin (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
ReserveMinted () int64
SetAdmin (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string)
SetDayCap (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, n int64)
SetMinter (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string)
SetPaused (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, on bool)
Settle (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64)
State (id int64) string
Sweep (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
Tally (id int64) (yes int64, no int64, abstain int64, total int64)
TextOnly (s string) interface {Error func() string}
Timings (id int64) (opened int64, closes int64, ready int64, expires int64)
TotalSupply () int64
Transfer (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, to string, amount int64)
TransferFrom (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, from string, to string, amount int64)
Vote (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64, choice string)
VoteOf (id int64, who string) (choice string, weight int64, ok bool)
VotesOf (who string) int64
VoteWithReason (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id int64, choice string, reason string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
)
9
10 // This realm's own powers, installed at init under the engine's reserved prefix (Offer refuses it, so nothing
11 // can impersonate one). Each is a setting the admin holds (admin.gno) put under the holders too, so the admin's
12 // every power can be taken away by vote — and one that is nobody's but theirs: the reserve.
13 //
14 // govern:admin "g1…" | "none" hand the settings to that address, or destroy the admin for good
15 // govern:minter "g1…" | "none" name who mints referrals, or nobody
16 // govern:pause "on" | "off" stop or resume the referral mint
17 // govern:daycap N the daily ceiling, in whole BUBBLE
18 // govern:reserve "g1… N" mint N whole BUBBLE from the reserve to that address (the only way it moves)
19 // govern:signal text a question with no effect: the sense of the holders
20 //
21 // Describe reads the payload alone (two voters at different heights must see the same question); Check reads
22 // the world; Do changes it. A Do that returns an error finishes the proposal as failed; one that panics aborts
23 // the transaction and leaves it executable again.
24
25 const none = "none"
26
27 // The money kinds (everything but the signal) pass on kourt's own terms (Jae, room 201: "use the system from
28 // kourt.xyz … it should auto adjust"): a fifth of the supply turning out, two thirds of yes+no, a week to vote, two
29 // days' delay, a fortnight's grace — and the turnout bar ADJUSTS: it is the greater of that fifth and the mean
30 // turnout of the last few money votes (never above a third of the supply), kourt's min(X̄, votable/3) with the
31 // bootstrap fifth as the base. Nobody sets it; the holders' own habit does. A signal decides nothing and asks less.
32 const (
33 ReserveMaxPer = 2_000_000 * one // per proposal
34 ReserveGap = int64 ( 90 * 24 * 60 * 60 ) // seconds between two reserve mints: a full drain is 25 votes over 6 years
35 turnoutKeep = 8 // money votes the mean remembers
36 )
37
38 var (
39 reserveLast int64 // block time of the last reserve mint
40 turnouts [] int64 // the last turnoutKeep money votes' turnout (yes+no+abstain), newest last
41 noted = map [ int64 ] bool {}
42 )
43
44 func installKinds() {
45 money := governor.BootstrapRules()
46 engine.InstallBuiltin(adminKind{}, money)
47 engine.InstallBuiltin(minterKind{}, money)
48 engine.InstallBuiltin(pauseKind{}, money)
49 engine.InstallBuiltin(daycapKind{}, money)
50 engine.InstallBuiltin(reserveKind{}, money)
51 // A signal decides nothing: a lighter bar, so the holders can be asked often.
52 engine.InstallBuiltin(signalKind{}, governor.NewRules( 500 , 5000 , 3 * 24 * 60 * 60 / 5 , 0 , 14 * 24 * 60 * 60 / 5 , 0 ))
53 }
54
55 func isMoneyKind(kind string ) bool {
56 switch kind {
57 case governor.Reserved + "reserve" , governor.Reserved + "admin" , governor.Reserved + "minter" , governor.Reserved + "pause" , governor.Reserved + "daycap" :
58 return true
59 }
60 return false
61 }
62
63 // quorumFloor is the absolute turnout a money kind needs at the last sealed epoch: max(supply/5, min(X̄, supply/3)),
64 // X̄ the mean turnout of the last money votes. Zero for the rest (the rules' fraction alone).
65 func quorumFloor(kind string ) int64 {
66 if !isMoneyKind(kind) {
67 return 0
68 }
69 at := ledger.Epoch() - 1
70 if at == 0 {
71 return 0 // the engine refuses the proposal itself: no sealed epoch yet
72 }
73 supply := ledger.PastTotal(at)
74 floor := supply / 5
75 if x := meanTurnout(); x > floor {
76 if third := supply / 3 ; x > third {
77 x = third
78 }
79 if x > floor {
80 floor = x
81 }
82 }
83 return floor
84 }
85
86 func meanTurnout() int64 {
87 if len(turnouts) == 0 {
88 return 0
89 }
90 var sum int64
91 for _, t := range turnouts {
92 sum += t
93 }
94 return sum / int64 (len(turnouts))
95 }
96
97 // noteTurnout remembers a decided money vote's turnout, once, when it is settled or executed.
98 func noteTurnout(id int64 ) {
99 if noted[id] {
100 return
101 }
102 switch engine.State(id) {
103 case "active" , "canceled" :
104 return
105 }
106 yes, no, abstain, _ := engine.Tally(id)
107 noted[id] = true
108 turnouts = append(turnouts, yes+no+abstain)
109 if len(turnouts) > turnoutKeep {
110 turnouts = turnouts[len(turnouts)-turnoutKeep:]
111 }
112 if len(noted) > 256 { // the map is a guard, not a record: a fresh one loses nothing that matters
113 noted = map [ int64 ] bool {}
114 }
115 }
116
117 // MeanTurnout is what the bar follows, for the page and the box.
118 func MeanTurnout() int64 { return meanTurnout() }
119
120 type bubbleErr struct { msg string }
121
122 func (e *bubbleErr) Error() string { return "bubble: " + e.msg }
123 func errf(s string ) error { return &bubbleErr{s} }
124
125 func addrOrNone(payload, what string ) error {
126 if payload == none {
127 return nil
128 }
129 if !address(payload).IsValid() {
130 return errf( "not an address, and not \"" + none + "\"" )
131 }
132 return nil
133 }
134
135 // ---- govern:admin
136
137 type adminKind struct {}
138
139 func (adminKind) Name() string { return governor.Reserved + "admin" }
140 func (adminKind) Describe(p string ) string {
141 if p == none {
142 return "destroy the admin — every setting moves only by vote from then on"
143 }
144 return "hand the settings (minter, pause, day cap) to " + p
145 }
146 func (adminKind) Check(p string ) error {
147 if err := addrOrNone(p, "admin" ); err != nil {
148 return err
149 }
150 if p != none && address(p) == admin {
151 return errf( "that is already the admin" )
152 }
153 if p == none && admin == "" {
154 return errf( "the admin is already gone" )
155 }
156 return nil
157 }
158 func (adminKind) Do(_ int , rlm realm, p string ) error {
159 if p == none {
160 admin = ""
161 return nil
162 }
163 admin = address(p)
164 return nil
165 }
166
167 // ---- govern:minter
168
169 type minterKind struct {}
170
171 func (minterKind) Name() string { return governor.Reserved + "minter" }
172 func (minterKind) Describe(p string ) string {
173 if p == none {
174 return "name nobody as minter — the referral mint stops until a vote names one"
175 }
176 return "name " + p + " as the minter of referral rewards"
177 }
178 func (minterKind) Check(p string ) error {
179 if err := addrOrNone(p, "minter" ); err != nil {
180 return err
181 }
182 if p != none && address(p) == minter {
183 return errf( "that is already the minter" )
184 }
185 return nil
186 }
187 func (minterKind) Do(_ int , rlm realm, p string ) error {
188 if p == none {
189 minter = ""
190 return nil
191 }
192 minter = address(p)
193 return nil
194 }
195
196 // ---- govern:pause
197
198 type pauseKind struct {}
199
200 func (pauseKind) Name() string { return governor.Reserved + "pause" }
201 func (pauseKind) Describe(p string ) string {
202 if p == "on" {
203 return "pause the referral mint"
204 }
205 return "resume the referral mint"
206 }
207 func (pauseKind) Check(p string ) error {
208 if p != "on" && p != "off" {
209 return errf( "expected \"on\" or \"off\"" )
210 }
211 if (p == "on" ) == paused {
212 return errf( "the mint is already " + p)
213 }
214 return nil
215 }
216 func (pauseKind) Do(_ int , rlm realm, p string ) error {
217 paused = p == "on"
218 return nil
219 }
220
221 // ---- govern:daycap
222
223 type daycapKind struct {}
224
225 func (daycapKind) Name() string { return governor.Reserved + "daycap" }
226 func (daycapKind) Describe(p string ) string {
227 return "set the daily mint ceiling to " + p + " BUBBLE"
228 }
229 func (daycapKind) Check(p string ) error {
230 n, err := strconv.ParseInt(p, 10 , 64 )
231 if err != nil || n < 0 || n > ReferralPool/one {
232 return errf( "expected a number of whole BUBBLE, up to the referral pool" )
233 }
234 return nil
235 }
236 func (daycapKind) Do(_ int , rlm realm, p string ) error {
237 n, err := strconv.ParseInt(p, 10 , 64 )
238 if err != nil {
239 return errf( "not a number" )
240 }
241 dayCap = n * one
242 return nil
243 }
244
245 // ---- govern:reserve
246
247 type reserveKind struct {}
248
249 func (reserveKind) Name() string { return governor.Reserved + "reserve" }
250 func (reserveKind) Describe(p string ) string {
251 to, n, err := parseReserve(p)
252 if err != nil {
253 return "mint from the reserve: " + p
254 }
255 return "mint " + strconv.FormatInt(n, 10 ) + " BUBBLE from the reserve to " + to.String()
256 }
257 func (reserveKind) Check(p string ) error {
258 _, n, err := parseReserve(p)
259 if err != nil {
260 return err
261 }
262 if reserveMinted+n*one > Reserve {
263 return errf( "only " + whole(Reserve-reserveMinted) + " BUBBLE are left in the reserve" )
264 }
265 if reserveLast != 0 && ledger.Now() < reserveLast+ReserveGap {
266 return errf( "the reserve moved less than ninety days ago; the next mint can run after " + strconv.FormatInt(reserveLast+ReserveGap, 10 ))
267 }
268 return nil
269 }
270 func (reserveKind) Do(_ int , rlm realm, p string ) error {
271 to, n, err := parseReserve(p)
272 if err != nil {
273 return err
274 }
275 if reserveMinted+n*one > Reserve || (reserveLast != 0 && ledger.Now() < reserveLast+ReserveGap) {
276 return errf( "the reserve is not open to this mint" ) // Check ran just before; a batch member could have changed the world
277 }
278 reserveMinted += n * one
279 reserveLast = ledger.Now()
280 ledger.Mint(to, n*one)
281 return nil
282 }
283
284 func parseReserve(p string ) (address, int64 , error ) {
285 parts := strings.Fields(p)
286 if len(parts) != 2 {
287 return "" , 0 , errf( "expected \"g1… N\": an address and whole BUBBLE" )
288 }
289 to := address(parts[ 0 ])
290 if !to.IsValid() {
291 return "" , 0 , errf( "not an address" )
292 }
293 n, err := strconv.ParseInt(parts[ 1 ], 10 , 64 )
294 if err != nil || n <= 0 || n > ReserveMaxPer/one {
295 return "" , 0 , errf( "expected whole BUBBLE, between 1 and " + whole(ReserveMaxPer) + " in one proposal" )
296 }
297 return to, n, nil
298 }
299
300 // ---- govern:signal
301
302 type signalKind struct {}
303
304 func (signalKind) Name() string { return governor.Reserved + "signal" }
305 func (signalKind) Describe(p string ) string { return "signal (no effect): " + p }
306 func (signalKind) Check(p string ) error {
307 if p == "" {
308 return errf( "a signal needs words" )
309 }
310 if len(p) > 1000 {
311 return errf( "a signal is under a thousand characters" )
312 }
313 return governor.TextOnly(p)
314 }
315 func (signalKind) Do(_ int , rlm realm, p string ) error { return nil }
316
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.