Search Apps Documentation Source Content File Folder Download Copy Actions Download

prop_requests.gno

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