prop_requests.gno
6.59 Kb ยท 232 lines
1package impl
2
3import (
4 "chain/runtime"
5
6 "gno.land/p/aeddi/panictoerr"
7 "gno.land/p/moul/md"
8 trs_pkg "gno.land/p/nt/treasury"
9 "gno.land/p/nt/ufmt"
10
11 "gno.land/r/gov/dao"
12 "gno.land/r/gov/dao/v3/memberstore"
13 "gno.land/r/gov/dao/v3/treasury"
14)
15
16func NewChangeLawRequest(_ realm, newLaw *Law) dao.ProposalRequest {
17 member, _ := memberstore.Get().GetMember(runtime.OriginCaller())
18 if member == nil {
19 panic("proposer is not a member")
20 }
21
22 cb := func(_ realm) error {
23 law = newLaw
24 return nil
25 }
26
27 e := dao.NewSimpleExecutor(cb, ufmt.Sprintf("A new Law is proposed:\n %v", newLaw))
28
29 return dao.NewProposalRequest("Change Law Proposal", "This proposal is looking to change the actual govDAO Law", e)
30}
31
32func NewUpgradeDaoImplRequest(newDao dao.DAO, realmPkg, reason string) dao.ProposalRequest {
33 member, _ := memberstore.Get().GetMember(runtime.OriginCaller())
34 if member == nil {
35 panic("proposer is not a member")
36 }
37
38 cb := func(_ realm) error {
39 // dao.UpdateImpl() must be cross-called from v3/impl but
40 // what calls this cb function is r/gov/dao.
41 // therefore we must cross back into v3/impl and then
42 // cross call dao.UpdateRequest().
43 dao.UpdateImpl(cross, dao.UpdateRequest{
44 DAO: newDao,
45 AllowedDAOs: []string{"gno.land/r/gov/dao/v3/impl", realmPkg}, // keeping previous realm just in case something went wrong
46 })
47 return nil
48 }
49
50 e := dao.NewSimpleExecutor(cb, "")
51
52 return dao.NewProposalRequest("Change DAO implementation", "This proposal is looking to change the actual govDAO implementation. Reason: "+reason, e)
53}
54
55func NewAddMemberRequest(_ realm, addr address, tier string, portfolio string) dao.ProposalRequest {
56 _, ok := memberstore.Tiers.GetTier(tier)
57 if !ok {
58 panic("provided tier does not exists")
59 }
60
61 if tier != memberstore.T1 && tier != memberstore.T2 {
62 panic("Only T1 and T2 members can be added by proposal. To add a T3 member use AddMember function directly.")
63 }
64
65 if portfolio == "" {
66 panic("A portfolio for the proposed member is required")
67 }
68
69 member, _ := memberstore.Get().GetMember(runtime.OriginCaller())
70 if member == nil {
71 panic("proposer is not a member")
72 }
73
74 if member.InvitationPoints <= 0 {
75 panic("proposer does not have enough invitation points for inviting new people to the board")
76 }
77
78 cb := func(_ realm) error {
79 member.RemoveInvitationPoint()
80 err := memberstore.Get().SetMember(tier, addr, memberByTier(tier))
81
82 return err
83 }
84
85 e := dao.NewSimpleExecutor(cb, ufmt.Sprintf("A new member with address %v is proposed to be on tier %v. Provided Portfolio information:\n\n%v", addr, tier, portfolio))
86
87 name := tryResolveAddr(addr)
88 return dao.NewProposalRequestWithFilter(
89 ufmt.Sprintf("New %s Member Proposal", tier),
90 ufmt.Sprintf("This is a proposal to add `%s` to **%s**.\n#### `%s`'s Portfolio:\n\n%s\n", name, tier, name, portfolio),
91 e,
92 FilterByTier{Tier: tier},
93 )
94}
95
96func NewWithdrawMemberRequest(_ realm, addr address, reason string) dao.ProposalRequest {
97 member, tier := memberstore.Get().GetMember(addr)
98 if member == nil {
99 panic("user we want to remove not found")
100 }
101
102 if tier == memberstore.T1 && reason == "" {
103 panic("T1 user removals must contains a reason.")
104 }
105
106 cb := func(_ realm) error {
107 memberstore.Get().RemoveMember(addr)
108 return nil
109 }
110
111 e := dao.NewSimpleExecutor(cb, ufmt.Sprintf("Member with address %v will be withdrawn.\n\n REASON: %v.", addr, reason))
112
113 return dao.NewProposalRequest(
114 "Member Withdrawal Proposal",
115 ufmt.Sprintf("This is a proposal to remove %s from the GovDAO", tryResolveAddr(addr)),
116 e,
117 )
118}
119
120func NewPromoteMemberRequest(addr address, fromTier string, toTier string) dao.ProposalRequest {
121 cb := func(_ realm) error {
122 prevTier := memberstore.Get().RemoveMember(addr)
123 if prevTier == "" {
124 panic("member not found, so cannot be promoted")
125 }
126
127 if prevTier != fromTier {
128 panic("previous tier changed from the one indicated in the proposal")
129 }
130
131 err := memberstore.Get().SetMember(toTier, addr, memberByTier(toTier))
132
133 return err
134 }
135
136 e := dao.NewSimpleExecutor(cb, ufmt.Sprintf("A new member with address %v will be promoted from tier %v to tier %v.", addr, fromTier, toTier))
137
138 return dao.NewProposalRequestWithFilter(
139 "Member Promotion Proposal",
140 ufmt.Sprintf("This is a proposal to promote %s from **%s** to **%s**.", tryResolveAddr(addr), fromTier, toTier),
141 e,
142 FilterByTier{Tier: toTier},
143 )
144}
145
146func NewTreasuryPaymentRequest(payment trs_pkg.Payment, reason string) dao.ProposalRequest {
147 if !treasury.HasBanker(payment.BankerID()) {
148 panic("banker not registered in treasury with ID: " + payment.BankerID())
149 }
150
151 if reason == "" {
152 panic("treasury payment request requires a reason")
153 }
154
155 cb := func(_ realm) error {
156 return panictoerr.PanicToError(func() {
157 treasury.Send(cross, payment)
158 })
159 }
160
161 e := dao.NewSimpleExecutor(
162 cb,
163 ufmt.Sprintf(
164 "A payment will be sent by the GovDAO treasury.\n\nReason: %s\n\nPayment: %s.",
165 reason,
166 payment.String(),
167 ),
168 )
169
170 return dao.NewProposalRequest(
171 "Treasury Payment",
172 ufmt.Sprintf(
173 "This proposal is looking to send a payment using the treasury.\n\nReason: %s\n\nPayment: %s",
174 reason,
175 payment.String(),
176 ),
177 e,
178 )
179}
180
181// NewTreasuryGRC20TokensUpdate creates a proposal request to update the list of GRC20 tokens registry
182// keys used by the treasury. The new list, if voted and accepted, will overwrite the current one.
183func NewTreasuryGRC20TokensUpdate(newTokenKeys []string) dao.ProposalRequest {
184 cb := func(_ realm) error {
185 return panictoerr.PanicToError(func() {
186 // NOTE:: Consider checking if the newTokenKeys are already registered
187 // in the grc20reg before updating the treasury tokens keys.
188 treasury.SetTokenKeys(cross, newTokenKeys)
189 })
190 }
191
192 bulletList := md.BulletList(newTokenKeys)
193
194 e := dao.NewSimpleExecutor(
195 cb,
196 ufmt.Sprintf(
197 "The list of GRC20 tokens used by the treasury will be updated.\n\nNew Token Keys:\n%s.\n",
198 bulletList,
199 ),
200 )
201
202 return dao.NewProposalRequest(
203 "Treasury GRC20 Tokens Update",
204 ufmt.Sprintf(
205 "This proposal is looking to update the list of GRC20 tokens used by the treasury.\n\nNew Token Keys:\n%s",
206 bulletList,
207 ),
208 e,
209 )
210}
211
212func memberByTier(tier string) *memberstore.Member {
213 switch tier {
214 case memberstore.T1:
215 t, _ := memberstore.Tiers.GetTier(memberstore.T1)
216 return &memberstore.Member{
217 InvitationPoints: t.InvitationPoints,
218 }
219 case memberstore.T2:
220 t, _ := memberstore.Tiers.GetTier(memberstore.T2)
221 return &memberstore.Member{
222 InvitationPoints: t.InvitationPoints,
223 }
224 case memberstore.T3:
225 t, _ := memberstore.Tiers.GetTier(memberstore.T3)
226 return &memberstore.Member{
227 InvitationPoints: t.InvitationPoints,
228 }
229 default:
230 panic("member not found by the specified tier")
231 }
232}