Search Apps Documentation Source Content File Folder Download Copy Actions Download

prop_requests.gno

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