public_proposals.gno
3.87 Kb · 168 lines
1package commondao
2
3import (
4 "chain/runtime"
5 "strings"
6 "time"
7
8 "gno.land/p/moul/addrset"
9 "gno.land/p/nt/commondao/v0"
10)
11
12// CreateTextProposal creates a new general text proposal.
13//
14// Parameters:
15// - daoID: ID of the root DAO (required)
16// - title: Title of the proposal (required)
17// - body: Body of the proposal (required)
18// - votingDays: The number of days where proposal accepts votes.
19//
20// The default voting period is 7 days.
21func CreateTextProposal(_ realm, daoID uint64, title, body string, votingDays uint8) uint64 {
22 dao := mustGetDAO(daoID)
23 o := getOptions(daoID)
24 if !o.AllowTextProposals {
25 panic("forbidden")
26 }
27
28 if votingDays > 30 {
29 panic("maximum proposal voting period is 30 days")
30 }
31
32 caller := runtime.PreviousRealm().Address()
33 assertCallerIsMember(caller, dao)
34
35 var votingPeriod time.Duration
36 if votingDays == 0 {
37 votingPeriod = time.Hour * 24 * 7
38 } else {
39 votingPeriod = time.Hour * 24 * time.Duration(votingDays)
40 }
41
42 def := NewTextPropDefinition(title, body, commondao.QuorumOneThird, votingPeriod)
43 p, err := dao.Propose(caller, def)
44 if err != nil {
45 panic(err)
46 }
47
48 return p.ID()
49}
50
51// CreateMembersUpdateProposal creates a new proposal to add and/or remove DAO members.
52//
53// Parameters:
54// - daoID: ID of the root DAO (required)
55// - newMembers: Comma separated list of addresses to add as members
56// - removeMembers: Comma separated list of member addresses to remove
57func CreateMembersUpdateProposal(_ realm, daoID uint64, newMembers, removeMembers string) uint64 {
58 dao := mustGetDAO(daoID)
59 o := getOptions(daoID)
60 if !o.AllowMembersUpdate {
61 panic("forbidden")
62 }
63
64 caller := runtime.PreviousRealm().Address()
65 assertCallerIsMember(caller, dao)
66
67 toAdd := mustParseStringToAddrset(newMembers)
68 toRemove := mustParseStringToAddrset(removeMembers)
69 def := newMembersUpdatePropDefinition(dao, toAdd, toRemove)
70 p, err := dao.Propose(caller, def)
71 if err != nil {
72 panic(err)
73 }
74
75 if err = p.Validate(); err != nil {
76 panic(err)
77 }
78
79 return p.ID()
80}
81
82// CreateSubDAOProposal creates a new proposal to create a new SubDAO.
83//
84// Parameters:
85// - daoID: ID of the parent DAO (required)
86// - name: A name for the SubDAO (required)
87// - members: Comma separated list of addresses to add as members
88func CreateSubDAOProposal(_ realm, daoID uint64, name, members string) uint64 {
89 dao := mustGetDAO(daoID)
90 o := getOptions(daoID)
91 if !(o.AllowSubDAOProposals && o.AllowChildren) {
92 panic("forbidden")
93 }
94
95 caller := runtime.PreviousRealm().Address()
96 assertCallerIsMember(caller, dao)
97
98 daoMembers := mustParseStringToAddrset(members)
99 def := newSubDAOPropDefinition(dao, name, &daoMembers)
100 p, err := dao.Propose(caller, def)
101 if err != nil {
102 panic(err)
103 }
104
105 if err = p.Validate(); err != nil {
106 panic(err)
107 }
108
109 return p.ID()
110}
111
112// CreateDissolutionProposal creates a new proposal to dissolve a DAO or SubDAO.
113//
114// SubDAOs can only be dissolveed by the the parent DAO.
115//
116// Parameters:
117// - daoID: ID of the DAO to dissolve (required)
118func CreateDissolutionProposal(_ realm, daoID uint64) uint64 {
119 // When DAO to dissolve is a SubDAO make sure that proposal is created in the parent DAO
120 dao := mustGetDAO(daoID)
121 dissolveDAO := dao
122 if parent := dao.Parent(); parent != nil {
123 dao = parent
124 }
125
126 o := getOptions(dao.ID())
127 if !o.AllowDissolutionProposals {
128 panic("forbidden")
129 }
130
131 caller := runtime.PreviousRealm().Address()
132 assertCallerIsMember(caller, dao)
133
134 def := newDissolvePropDefinition(dissolveDAO)
135 p, err := dao.Propose(caller, def)
136 if err != nil {
137 panic(err)
138 }
139
140 if err = p.Validate(); err != nil {
141 panic(err)
142 }
143
144 return p.ID()
145}
146
147func mustParseStringToAddrset(s string) (set addrset.Set) {
148 if s == "" {
149 return
150 }
151
152 for _, raw := range strings.Split(s, "\n") {
153 raw = strings.TrimSpace(raw)
154 if raw == "" {
155 continue
156 }
157
158 addr := address(raw)
159 if !addr.IsValid() {
160 panic("invalid address: " + addr.String())
161 }
162
163 if !set.Has(addr) {
164 set.Add(addr)
165 }
166 }
167 return set
168}