Skip to content

Commit

Permalink
feat(comondao): change commondao package to use addrset for membe…
Browse files Browse the repository at this point in the history
…rs (#3813)

Changes `CommonDAO` members to use `gno.land/p/moul/addrset`to store
members.

This allows to use an address set for members which can optionally be
stored in a separate realm if needed.

It also simplifies the `commondao` package code reducing the number of
methods required to deal with member management.
  • Loading branch information
jeronimoalbi authored Feb 24, 2025
1 parent 3513776 commit ec9eacc
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 50 deletions.
39 changes: 7 additions & 32 deletions examples/gno.land/p/nt/commondao/commondao.gno
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
"gno.land/p/demo/avl"
"gno.land/p/demo/avl/rotree"
"gno.land/p/demo/seqid"
"gno.land/p/moul/addrset"
)

var (
ErrInvalidVoteChoice = errors.New("invalid vote choice")
ErrMemberExists = errors.New("member already exist")
ErrNotMember = errors.New("account is not a member of the DAO")
ErrOverflow = errors.New("next ID overflows uint64")
ErrProposalNotFound = errors.New("proposal not found")
Expand All @@ -24,7 +24,7 @@ type (
// CommonDAO defines a DAO.
CommonDAO struct {
parent *CommonDAO
members *avl.Tree // string(std.Address) -> struct{}
members *addrset.Set
genID seqid.ID
active *avl.Tree // string(proposal ID) -> *Proposal
finished *avl.Tree // string(proposal ID) -> *Proposal
Expand All @@ -41,7 +41,7 @@ type (
// New creates a new common DAO.
func New(options ...Option) *CommonDAO {
dao := &CommonDAO{
members: avl.NewTree(),
members: &addrset.Set{},
active: avl.NewTree(),
finished: avl.NewTree(),
}
Expand All @@ -58,33 +58,8 @@ func (dao CommonDAO) Parent() *CommonDAO {
}

// Members returns the list of DAO members.
func (dao CommonDAO) Members() []std.Address {
var members []std.Address
dao.members.Iterate("", "", func(key string, _ interface{}) bool {
members = append(members, std.Address(key))
return false
})
return members
}

// AddMember adds a new member to the DAO.
func (dao *CommonDAO) AddMember(user std.Address) error {
if dao.IsMember(user) {
return ErrMemberExists
}
dao.members.Set(user.String(), struct{}{})
return nil
}

// RemoveMember removes a member from the DAO.
func (dao *CommonDAO) RemoveMember(user std.Address) (removed bool) {
_, removed = dao.members.Remove(user.String())
return removed
}

// IsMember checks if a user is a member of the DAO.
func (dao CommonDAO) IsMember(user std.Address) bool {
return dao.members.Has(user.String())
func (dao CommonDAO) Members() *addrset.Set {
return dao.members
}

// ActiveProposals returns all active DAO proposals.
Expand Down Expand Up @@ -138,7 +113,7 @@ func (dao *CommonDAO) Vote(member std.Address, proposalID uint64, c VoteChoice)
return ErrInvalidVoteChoice
}

if !dao.IsMember(member) {
if !dao.Members().Has(member) {
return ErrNotMember
}

Expand All @@ -157,7 +132,7 @@ func (dao *CommonDAO) Tally(p *Proposal) Stats {
NayVotes: record.VoteCount(ChoiceNo),
}
votesCount := stats.YayVotes + stats.NayVotes
membersCount := len(dao.Members())
membersCount := dao.Members().Size()
stats.Abstained = membersCount - votesCount

percentage := float64(votesCount) / float64(membersCount)
Expand Down
38 changes: 22 additions & 16 deletions examples/gno.land/p/nt/commondao/commondao_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func TestNew(t *testing.T) {

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
membersCount := len(tc.members)
options := []Option{WithParent(tc.parent)}
for _, m := range tc.members {
options = append(options, WithMember(m))
Expand All @@ -54,39 +55,44 @@ func TestNew(t *testing.T) {
uassert.NotEqual(t, nil, dao.Parent())
}

urequire.Equal(t, len(tc.members), len(dao.Members()), "dao members")
for i, m := range dao.Members() {
uassert.Equal(t, tc.members[i], m)
}
urequire.Equal(t, membersCount, dao.Members().Size(), "dao members")

var i int
dao.Members().IterateByOffset(0, membersCount, func(addr std.Address) bool {
uassert.Equal(t, tc.members[i], addr)
i++
return false
})
})
}
}

func TestCommonDAOAddMember(t *testing.T) {
func TestCommonDAOMembersAdd(t *testing.T) {
member := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")
dao := New(WithMember("g1w4ek2u33ta047h6lta047h6lta047h6ldvdwpn"))

err := dao.AddMember(member)
urequire.NoError(t, err)
uassert.Equal(t, 2, len(dao.Members()))
uassert.True(t, dao.IsMember(member))
added := dao.Members().Add(member)
urequire.True(t, added)

uassert.Equal(t, 2, dao.Members().Size())
uassert.True(t, dao.Members().Has(member))

err = dao.AddMember(member)
uassert.ErrorIs(t, err, ErrMemberExists)
added = dao.Members().Add(member)
urequire.False(t, added)
}

func TestCommonDAORemoveMember(t *testing.T) {
func TestCommonDAOMembersRemove(t *testing.T) {
member := std.Address("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5")
dao := New(WithMember(member))

removed := dao.RemoveMember(member)
removed := dao.Members().Remove(member)
urequire.True(t, removed)

removed = dao.RemoveMember(member)
removed = dao.Members().Remove(member)
urequire.False(t, removed)
}

func TestCommonDAOIsMember(t *testing.T) {
func TestCommonDAOMembersHas(t *testing.T) {
cases := []struct {
name string
member std.Address
Expand All @@ -108,7 +114,7 @@ func TestCommonDAOIsMember(t *testing.T) {

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := tc.dao.IsMember(tc.member)
got := tc.dao.Members().Has(tc.member)
uassert.Equal(t, got, tc.want)
})
}
Expand Down
19 changes: 17 additions & 2 deletions examples/gno.land/p/nt/commondao/options.gno
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package commondao

import "std"
import (
"std"

"gno.land/p/moul/addrset"
)

// Option configures the CommonDAO.
type Option func(*CommonDAO)
Expand All @@ -15,6 +19,17 @@ func WithParent(p *CommonDAO) Option {
// WithMember assigns a member to the DAO.
func WithMember(addr std.Address) Option {
return func(dao *CommonDAO) {
dao.members.Set(addr.String(), struct{}{})
dao.members.Add(addr)
}
}

// WithMembers assigns multiple members to the DAO.
// An empty member set is used by default when specified members set is nil.
func WithMembers(members *addrset.Set) Option {
return func(dao *CommonDAO) {
if members == nil {
members = &addrset.Set{}
}
dao.members = members
}
}

0 comments on commit ec9eacc

Please sign in to comment.