Skip to content

Commit

Permalink
feat: change commondaopackage to use addrset for members
Browse files Browse the repository at this point in the history
This allows to use an address set for members which can optionally be
stored in a separate realm if needed.
  • Loading branch information
jeronimoalbi committed Feb 24, 2025
1 parent 3513776 commit abae3e8
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 abae3e8

Please sign in to comment.