Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}