close.gno
2.45 Kb · 71 lines
1package bolo
2
3import (
4 "chain"
5 "chain/runtime"
6)
7
8// Close closes campaign id to new donations. Its organizer can, at any time; anyone
9// can, once it is past its deadline or inactive for long enough — the campaign's page
10// says when. Closing stops gifts only: what the campaign holds still goes to the
11// beneficiary, or back to its donors.
12//
13// Its branch follows state, never the caller (API §3.1, Q7). Past its deadline, Close
14// records the close if nothing has yet, and never fails. A dormant campaign closes as
15// dormant, whoever calls. Only an open campaign nobody else may close yet takes the
16// organizer's branch, which checks how the call arrived (C11) before it reads who
17// sent it.
18func Close(cur realm, id string) {
19 assertCurrent(cur)
20 c := campaignOf(id)
21 recordDeadline(cur, c)
22 if c.deadline != 0 && now() >= c.deadline {
23 return // recorded above, now or before: housekeeping, never an error
24 }
25 assertOpen(c)
26 if now() >= dormantAt(c) {
27 closeNow(c, reasonDormant, cur.Previous().Address())
28 return
29 }
30 if !cur.Previous().IsUserCall() {
31 panic(ErrNotUserCall)
32 }
33 runtime.AssertOriginCall()
34 if cur.Previous().Address() != c.organizer {
35 panic(ErrNotClosable)
36 }
37 closeNow(c, reasonOrganizer, c.organizer)
38}
39
40// closeNow closes the open campaign c now, for reason, and takes it out of the
41// active indexes; Closed names by, who sent the call (API §3.1, §6).
42func closeNow(c *campaign, reason string, by address) {
43 s := c.state
44 s.status, s.closedAt, s.closeReason = statusClosed, now(), reason
45 stats.open--
46 dropActive(c)
47 chain.Emit(evClosed, "id", idString(c.id), "reason", reason, "by", by.String())
48}
49
50// closeNotYet is CanClose's reason while only the organizer may close.
51const closeNotYet = "not_yet"
52
53// CanClose says what Close(id), sent by caller from a wallet, would do now (API
54// §4): true, with the reason it would record — "deadline" or "dormant" for anyone,
55// "organizer" for the organizer alone — or false, with "closed" once closed or
56// settled, and "not_yet" while only the organizer may close.
57func CanClose(id string, caller address) (bool, string) {
58 c := campaignOf(id)
59 status, _, _ := effective(c)
60 switch {
61 case status != statusOpen && c.state.status == statusOpen:
62 return true, reasonDeadline // passed, not yet recorded
63 case status != statusOpen:
64 return false, statusClosed
65 case now() >= dormantAt(c):
66 return true, reasonDormant
67 case caller == c.organizer:
68 return true, reasonOrganizer
69 }
70 return false, closeNotYet
71}