types.gno
4.67 Kb ยท 222 lines
1package impl
2
3import (
4 "strings"
5
6 "gno.land/p/nt/avl"
7 "gno.land/p/nt/ufmt"
8 "gno.land/r/gov/dao"
9 "gno.land/r/gov/dao/v3/memberstore"
10)
11
12type Law struct {
13 Supermajority float64
14}
15
16func (l *Law) String() string {
17 return ufmt.Sprintf("This law contains the following data:\n\n- Supermajority: %v%%", l.Supermajority)
18}
19
20// ProposalsStatuses contains the status of all the proposals indexed by the proposal ID.
21type ProposalsStatuses struct {
22 *avl.Tree // map[int]*proposalStatus
23}
24
25func NewProposalsStatuses() ProposalsStatuses {
26 return ProposalsStatuses{avl.NewTree()}
27}
28
29func (pss ProposalsStatuses) GetStatus(id dao.ProposalID) *proposalStatus {
30 pids := id.String()
31 psv, ok := pss.Get(pids)
32 if !ok {
33 return nil
34 }
35
36 ps, ok := psv.(*proposalStatus)
37 if !ok {
38 panic("ProposalsStatuses must contains only proposalStatus types")
39 }
40
41 return ps
42}
43
44type proposalStatus struct {
45 YesVotes memberstore.MembersByTier
46 NoVotes memberstore.MembersByTier
47 AllVotes memberstore.MembersByTier
48
49 Accepted bool
50 Denied bool
51
52 DeniedReason string
53
54 TiersAllowedToVote []string
55
56 TotalPower float64 // TotalPower is the power of all the members existing when this proposal was created.
57}
58
59func getMembers(cur realm) memberstore.MembersByTier {
60 return memberstore.Get()
61}
62
63func newProposalStatus(allowedToVote []string) *proposalStatus {
64 yv := memberstore.NewMembersByTier()
65 yv.SetTier(memberstore.T1)
66 yv.SetTier(memberstore.T2)
67 yv.SetTier(memberstore.T3)
68 nv := memberstore.NewMembersByTier()
69 nv.SetTier(memberstore.T1)
70 nv.SetTier(memberstore.T2)
71 nv.SetTier(memberstore.T3)
72 av := memberstore.NewMembersByTier()
73 av.SetTier(memberstore.T1)
74 av.SetTier(memberstore.T2)
75 av.SetTier(memberstore.T3)
76
77 return &proposalStatus{
78 YesVotes: yv,
79 NoVotes: nv,
80 AllVotes: av,
81
82 TiersAllowedToVote: allowedToVote,
83
84 TotalPower: getMembers(cross).GetTotalPower(),
85 }
86}
87
88func (ps *proposalStatus) YesPercent() float64 {
89 var yp float64
90
91 memberstore.Tiers.Iterate("", "", func(tn string, ti interface{}) bool {
92 tier, ok := ti.(memberstore.Tier)
93 if !ok {
94 panic("type must be memberstore.Tier")
95 }
96
97 power := tier.PowerHandler(getMembers(cross), memberstore.Tiers)
98 ts := ps.YesVotes.GetTierSize(tn)
99
100 yp = yp + (power * float64(ts))
101
102 return false
103 })
104
105 return (yp / ps.TotalPower) * 100
106}
107
108func (ps *proposalStatus) NoPercent() float64 {
109 var np float64
110
111 memberstore.Tiers.Iterate("", "", func(tn string, ti interface{}) bool {
112 tier, ok := ti.(memberstore.Tier)
113 if !ok {
114 panic("type must be memberstore.Tier")
115 }
116
117 power := tier.PowerHandler(getMembers(cross), memberstore.Tiers)
118 ts := ps.NoVotes.GetTierSize(tn)
119 np = np + (power * float64(ts))
120
121 return false
122 })
123
124 return (np / ps.TotalPower) * 100
125}
126
127func (ps *proposalStatus) IsAllowed(tier string) bool {
128 for _, ta := range ps.TiersAllowedToVote {
129 if ta == tier {
130 return true
131 }
132 }
133
134 return false
135}
136
137func (ps *proposalStatus) String() string {
138 var sb strings.Builder
139 sb.WriteString("### Stats\n")
140
141 if ps.Accepted {
142 sb.WriteString("- **PROPOSAL HAS BEEN ACCEPTED**\n")
143 } else if ps.Denied {
144 sb.WriteString("- **PROPOSAL HAS BEEN DENIED**\n")
145 if ps.DeniedReason != "" {
146 sb.WriteString("REASON: ")
147 sb.WriteString(ps.DeniedReason)
148 }
149 } else {
150 sb.WriteString("- **Proposal is open for votes**\n")
151 }
152
153 sb.WriteString("- Tiers eligible to vote: ")
154 sb.WriteString(strings.Join(ps.TiersAllowedToVote, ", "))
155 sb.WriteString("\n")
156
157 sb.WriteString(ufmt.Sprintf("- YES PERCENT: %v%%\n", ps.YesPercent()))
158 sb.WriteString(ufmt.Sprintf("- NO PERCENT: %v%%\n", ps.NoPercent()))
159
160 return sb.String()
161}
162
163func StringifyVotes(ps *proposalStatus) string {
164 var sb strings.Builder
165
166 writeVotes(&sb, ps.YesVotes, "YES")
167 writeVotes(&sb, ps.NoVotes, "NO")
168
169 if sb.String() == "" {
170 return "No one voted yet."
171 }
172
173 return sb.String()
174}
175
176func writeVotes(sb *strings.Builder, t memberstore.MembersByTier, title string) {
177 if t.Size() == 0 {
178 return
179 }
180 t.Iterate("", "", func(tn string, value interface{}) bool {
181 tier, ok := memberstore.Tiers.GetTier(tn)
182 if !ok {
183 panic("tier not found")
184 }
185
186 power := tier.PowerHandler(getMembers(cross), memberstore.Tiers)
187
188 sb.WriteString(ufmt.Sprintf("%v from %v (VPPM %v):\n\n", title, tn, power))
189 ms, _ := value.(*avl.Tree)
190 ms.Iterate("", "", func(addr string, _ interface{}) bool {
191 sb.WriteString("\n")
192 sb.WriteString("- " + tryResolveAddr(address(addr)) + "\n")
193
194 return false
195 })
196
197 sb.WriteString("\n")
198
199 return false
200 })
201}
202
203func StringifyProposal(p *dao.Proposal) string {
204 out := ufmt.Sprintf(`
205### Title: %s
206
207### Proposed by: %s
208
209%s
210`, p.Title(), p.Author(), p.Description())
211
212 if p.ExecutorString() != "" {
213 out += ufmt.Sprintf(`
214This proposal contains the following metadata:
215
216%s
217`, p.ExecutorString())
218 }
219
220 return out
221
222}