@@ -2,12 +2,16 @@ package daocond
22
33import (
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
2735type 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)
4048func (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
4457func (m *govDaoCondThreshold) RenderJSON() *json.Node {
@@ -62,16 +75,20 @@ func (m *govDaoCondThresholdState) HandleEvent(_ Event, _ map[string]Vote) {
6275}
6376func (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
7794var _ 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
107124func (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