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