From abae3e8e6e31556b3a5120d4ae9309b70a84e132 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Mon, 24 Feb 2025 12:09:15 +0100 Subject: [PATCH] feat: change `commondao`package to use `addrset` for members This allows to use an address set for members which can optionally be stored in a separate realm if needed. --- .../gno.land/p/nt/commondao/commondao.gno | 39 ++++--------------- .../p/nt/commondao/commondao_test.gno | 38 ++++++++++-------- examples/gno.land/p/nt/commondao/options.gno | 19 ++++++++- 3 files changed, 46 insertions(+), 50 deletions(-) diff --git a/examples/gno.land/p/nt/commondao/commondao.gno b/examples/gno.land/p/nt/commondao/commondao.gno index ac57846c0d3..20f2d08a22d 100644 --- a/examples/gno.land/p/nt/commondao/commondao.gno +++ b/examples/gno.land/p/nt/commondao/commondao.gno @@ -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") @@ -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 @@ -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(), } @@ -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. @@ -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 } @@ -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) diff --git a/examples/gno.land/p/nt/commondao/commondao_test.gno b/examples/gno.land/p/nt/commondao/commondao_test.gno index b118841eb96..7120b01d64e 100644 --- a/examples/gno.land/p/nt/commondao/commondao_test.gno +++ b/examples/gno.land/p/nt/commondao/commondao_test.gno @@ -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)) @@ -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 @@ -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) }) } diff --git a/examples/gno.land/p/nt/commondao/options.gno b/examples/gno.land/p/nt/commondao/options.gno index 87dff3c0f79..68d9c21d77b 100644 --- a/examples/gno.land/p/nt/commondao/options.gno +++ b/examples/gno.land/p/nt/commondao/options.gno @@ -1,6 +1,10 @@ package commondao -import "std" +import ( + "std" + + "gno.land/p/moul/addrset" +) // Option configures the CommonDAO. type Option func(*CommonDAO) @@ -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 } }