Skip to content

Commit f228e26

Browse files
feat(gno/govdao): handle T3 abstention in govdao condition to match T2 adding specification (#1504)
1 parent eee40de commit f228e26

File tree

5 files changed

+163
-70
lines changed

5 files changed

+163
-70
lines changed

gno/p/daocond/cond_govdao.gno

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package daocond
22

33
import (
44
"errors"
5+
"strconv"
6+
"strings"
57

68
"gno.land/p/demo/json"
79
"gno.land/p/demo/ufmt"
810
)
911

10-
func GovDaoCondThreshold(threshold float64, hasRoleFn func(memberId string, role string) bool, usersWithRoleCountFn func(role string) uint32) Condition {
12+
var roleWeights = []float64{3.0, 2.0, 1.0}
13+
14+
func GovDaoCondThreshold(threshold float64, roles []string, hasRoleFn func(memberId string, role string) bool, usersWithRoleCountFn func(role string) uint32) Condition {
1115
if threshold <= 0 || threshold > 1 {
1216
panic(errors.New("invalid threshold"))
1317
}
@@ -17,15 +21,20 @@ func GovDaoCondThreshold(threshold float64, hasRoleFn func(memberId string, role
1721
if hasRoleFn == nil {
1822
panic(errors.New("nil hasRoleFn"))
1923
}
24+
if len(roles) > 3 {
25+
panic("the govdao condition handles at most 3 roles")
26+
}
2027
return &govDaoCondThreshold{
2128
threshold: threshold,
29+
roles: roles,
2230
hasRoleFn: hasRoleFn,
2331
usersWithRoleCountFn: usersWithRoleCountFn,
2432
}
2533
}
2634

2735
type govDaoCondThreshold struct {
2836
threshold float64
37+
roles []string
2938
hasRoleFn func(memberId string, role string) bool
3039
usersWithRoleCountFn func(role string) uint32
3140
}
@@ -36,9 +45,13 @@ func (m *govDaoCondThreshold) NewState() State {
3645
}
3746
}
3847

39-
// {threshold}% of {totalvotingpower} total voting power following these ({t1power} - T1, {t2power} T2, {t3power} T3)
4048
func (m *govDaoCondThreshold) Render() string {
41-
return ufmt.Sprintf("%g%% of total voting power (3.0 / T1, 2.0 / T2, 1.0 / T3)*", m.threshold*100)
49+
rolePowers := []string{}
50+
for i, role := range m.roles {
51+
weight := strconv.FormatFloat(roleWeights[i], 'f', 2, 64) // ufmt.Sprintf("%.2f", ...) is not working
52+
rolePowers = append(rolePowers, ufmt.Sprintf("%s => %s power", role, weight))
53+
}
54+
return ufmt.Sprintf("%g%% of total voting power | %s", m.threshold*100, strings.Join(rolePowers, " | "))
4255
}
4356

4457
func (m *govDaoCondThreshold) RenderJSON() *json.Node {
@@ -62,16 +75,20 @@ func (m *govDaoCondThresholdState) HandleEvent(_ Event, _ map[string]Vote) {
6275
}
6376
func (m *govDaoCondThresholdState) RenderJSON(votes map[string]Vote) *json.Node {
6477
vPowers, totalPower := m.computeVotingPowers()
65-
return json.ObjectNode("", map[string]*json.Node{
78+
rolePowers := []string{}
79+
for _, role := range m.cond.roles {
80+
weight := strconv.FormatFloat(vPowers[role], 'f', 2, 64) // ufmt.Sprintf("%.2f", ...) is not working
81+
rolePowers = append(rolePowers, ufmt.Sprintf("%s => %s power", role, weight))
82+
}
83+
jsonData := json.ObjectNode("", map[string]*json.Node{
6684
"type": json.StringNode("", "govdao-threshold"),
67-
"treshold": json.NumberNode("", m.cond.threshold),
68-
"tier1VotingPower": json.NumberNode("", vPowers[roleT1]),
69-
"tier2VotingPower": json.NumberNode("", vPowers[roleT2]),
70-
"tier3VotingPower": json.NumberNode("", vPowers[roleT3]),
85+
"threshold": json.NumberNode("", m.cond.threshold),
86+
"powerSplit": json.StringNode("", strings.Join(rolePowers, " | ")),
7187
"totalYes": json.NumberNode("", m.yesRatio(votes)),
7288
"votingPowerNeeded": json.NumberNode("", m.cond.threshold*totalPower),
7389
"totalVotingPower": json.NumberNode("", totalPower),
7490
})
91+
return jsonData
7592
}
7693

7794
var _ State = (*govDaoCondThresholdState)(nil)
@@ -88,15 +105,15 @@ func (m *govDaoCondThresholdState) yesRatio(votes map[string]Vote) float64 {
88105
if vote != VoteYes {
89106
continue
90107
}
91-
tier := m.getUserTier(userID)
108+
tier := m.getUserRole(userID)
92109

93110
totalYes += votingPowersByTier[tier]
94111
}
95112
return totalYes / totalPower
96113
}
97114

98-
func (m *govDaoCondThresholdState) getUserTier(userID string) string {
99-
for _, role := range []string{roleT1, roleT2, roleT3} {
115+
func (m *govDaoCondThresholdState) getUserRole(userID string) string {
116+
for _, role := range m.cond.roles {
100117
if m.cond.hasRoleFn(userID, role) {
101118
return role
102119
}
@@ -105,16 +122,22 @@ func (m *govDaoCondThresholdState) getUserTier(userID string) string {
105122
}
106123

107124
func (m *govDaoCondThresholdState) computeVotingPowers() (map[string]float64, float64) {
108-
totalT1s := float64(m.cond.usersWithRoleCountFn(roleT1))
109-
totalT2s := float64(m.cond.usersWithRoleCountFn(roleT2))
110-
totalT3s := float64(m.cond.usersWithRoleCountFn(roleT3))
111-
votingPowers := map[string]float64{
112-
roleT1: 3.0,
113-
roleT2: computePower(totalT1s, totalT2s, 2.0),
114-
roleT3: computePower(totalT1s, totalT3s, 1.0),
125+
votingPowers := make(map[string]float64)
126+
totalPower := 0.0
127+
countsMembersPerRole := make(map[string]float64)
128+
129+
for _, role := range m.cond.roles {
130+
countsMembersPerRole[role] = float64(m.cond.usersWithRoleCountFn(role))
115131
}
116132

117-
totalPower := votingPowers[roleT1]*totalT1s + votingPowers[roleT2]*totalT2s + votingPowers[roleT3]*totalT3s
133+
for i, role := range m.cond.roles {
134+
if i == 0 {
135+
votingPowers[role] = roleWeights[0] // Highest tier always gets max power (3.0)
136+
} else {
137+
votingPowers[role] = computePower(countsMembersPerRole[m.cond.roles[0]], countsMembersPerRole[role], roleWeights[i])
138+
}
139+
totalPower += votingPowers[role] * countsMembersPerRole[role]
140+
}
118141

119142
return votingPowers, totalPower
120143
}
@@ -138,9 +161,3 @@ func computePower(T1, Tn, maxPower float64) float64 {
138161
}
139162
return computedPower
140163
}
141-
142-
const (
143-
roleT1 = "T1"
144-
roleT2 = "T2"
145-
roleT3 = "T3"
146-
)

0 commit comments

Comments
 (0)