Skip to content

Commit

Permalink
feat(daokit): add govdao threshold condition (#1496)
Browse files Browse the repository at this point in the history
Co-authored-by: n0izn0iz <[email protected]>
  • Loading branch information
Villaquiranm and n0izn0iz authored Feb 6, 2025
1 parent 042bb09 commit 607c315
Show file tree
Hide file tree
Showing 3 changed files with 411 additions and 0 deletions.
138 changes: 138 additions & 0 deletions gno/p/daocond/cond_govdao.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package daocond

import (
"errors"

"gno.land/p/demo/json"
"gno.land/p/demo/ufmt"
)

func GovDaoCondThreshold(threshold float64, hasRoleFn func(memberId string, role string) bool, usersWithRoleCountFn func(role string) uint64) Condition {
if threshold <= 0 || threshold > 1 {
panic(errors.New("invalid threshold"))
}
if usersWithRoleCountFn == nil {
panic(errors.New("nil usersWithRoleCountFn"))
}
if hasRoleFn == nil {
panic(errors.New("nil hasRoleFn"))
}
return &govDaoCondThreshold{
threshold: threshold,
hasRoleFn: hasRoleFn,
usersWithRoleCountFn: usersWithRoleCountFn,
}
}

type govDaoCondThreshold struct {
threshold float64
hasRoleFn func(memberId string, role string) bool
usersWithRoleCountFn func(role string) uint64
}

func (m *govDaoCondThreshold) NewState() State {
return &govDaoCondThresholdState {
cond: m,
}
}

func (m *govDaoCondThreshold) Render() string {
return ufmt.Sprintf("%g%% of members", m.threshold*100)
}

func (m *govDaoCondThreshold) RenderJSON() *json.Node {
return json.ObjectNode("", map[string]*json.Node{
"type": json.StringNode("", "govdao-threshold"),
"threshold": json.NumberNode("", m.threshold),
})
}

var _ Condition = (*govDaoCondThreshold)(nil)

type govDaoCondThresholdState struct {
cond *govDaoCondThreshold
}

func (m *govDaoCondThresholdState) Eval(votes map[string]Vote) bool{
return m.yesRatio(votes) >= m.cond.threshold
}
func (m *govDaoCondThresholdState) HandleEvent(_ Event, _ map[string]Vote) {
panic(errors.New("not implemented"))
}
func (m *govDaoCondThresholdState) RenderJSON(votes map[string]Vote) *json.Node {
return json.ObjectNode("", map[string]*json.Node{
"type": json.StringNode("", "govdao-threshold"),
"totalYes": json.NumberNode("", m.yesRatio(votes)),
})
}

var _ State = (*govDaoCondThresholdState)(nil)

func (m *govDaoCondThresholdState) yesRatio(votes map[string]Vote) float64 {
var totalYes float64
votingPowersByTier, totalPower := m.computeVotingPowers()
// Case when there are zero T1s
if totalPower == 0.0 {
return totalPower
}

for userID, vote := range votes {
if vote != VoteYes {
continue
}
tier := m.getUserTier(userID)

totalYes += votingPowersByTier[tier]
}
return totalYes/totalPower
}

func (m *govDaoCondThresholdState) getUserTier(userID string) string{
for _,role := range []string{roleT1, roleT2, roleT3}{
if m.cond.hasRoleFn(userID, role){
return role
}
}
panic("No role found for user")
}

func (m *govDaoCondThresholdState) computeVotingPowers() (map[string]float64, float64) {
totalT1s := float64(m.cond.usersWithRoleCountFn(roleT1))
totalT2s := float64(m.cond.usersWithRoleCountFn(roleT2))
totalT3s := float64(m.cond.usersWithRoleCountFn(roleT3))
votingPowers := map[string]float64{
roleT1: 3.0,
roleT2: computePower(totalT1s, totalT2s, 2.0),
roleT3: computePower(totalT1s, totalT3s, 1.0),
}

totalPower := votingPowers[roleT1]*totalT1s + votingPowers[roleT2]*totalT2s + votingPowers[roleT3]*totalT3s

return votingPowers, totalPower
}

// max power here is the number of votes each tier gets when we have
// the same number of member on each tier
// T2 = 2.0 and T1 = 1.0 with the ration T1/Tn
// we compute the actual ratio
func computePower(T1, Tn, maxPower float64) float64{
// If there are 0 Tn (T2, T3) just return the max power
// we could also return 0.0 as voting power
if Tn <= 0.0{
return maxPower
}

computedPower := (T1/Tn)*maxPower
if computedPower >= maxPower{
// If computed power is bigger than the max, this happens if Tn is lower than T1
// cap the max power to max power.
return maxPower
}
return computedPower
}

const (
roleT1= "T1"
roleT2= "T2"
roleT3= "T3"
)
Loading

0 comments on commit 607c315

Please sign in to comment.