Search Apps Documentation Source Content File Folder Download Copy Actions Download

proposal_members.gno

3.39 Kb · 135 lines
  1package commondao
  2
  3import (
  4	"errors"
  5	"strings"
  6	"time"
  7
  8	"gno.land/p/moul/addrset"
  9	"gno.land/p/moul/md"
 10	"gno.land/p/nt/commondao/v0"
 11)
 12
 13// newMembersUpdatePropDefinition creates a new proposal definition for adding/removing DAO members.
 14func newMembersUpdatePropDefinition(dao *commondao.CommonDAO, add, remove addrset.Set) membersUpdatePropDefinition {
 15	if dao == nil {
 16		panic("DAO is required")
 17	}
 18
 19	if dao.Members().Size() == 0 {
 20		panic("a DAO with at least one member is required to create member update proposals")
 21	}
 22
 23	if add.Size() == 0 && remove.Size() == 0 {
 24		panic("no members were specified to be added or removed")
 25	}
 26
 27	return membersUpdatePropDefinition{
 28		dao:      dao,
 29		toAdd:    add,
 30		toRemove: remove,
 31	}
 32}
 33
 34// membersUpdatePropDefinition defines a proposal type for adding/removing DAO members.
 35type membersUpdatePropDefinition struct {
 36	dao             *commondao.CommonDAO
 37	toAdd, toRemove addrset.Set
 38}
 39
 40func (membersUpdatePropDefinition) Title() string               { return "Members Update" }
 41func (membersUpdatePropDefinition) VotingPeriod() time.Duration { return time.Hour * 24 * 7 }
 42
 43func (p membersUpdatePropDefinition) Body() string {
 44	var b strings.Builder
 45
 46	if p.toAdd.Size() > 0 {
 47		items := make([]string, 0, p.toAdd.Size())
 48		p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr address) bool {
 49			items = append(items, addr.String())
 50			return false
 51		})
 52
 53		b.WriteString(md.Paragraph(
 54			md.Bold("Members to Add:") + "\n" + md.BulletList(items),
 55		))
 56	}
 57
 58	if p.toRemove.Size() > 0 {
 59		items := make([]string, 0, p.toRemove.Size())
 60		p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr address) bool {
 61			items = append(items, addr.String())
 62			return false
 63		})
 64
 65		b.WriteString(md.Paragraph(
 66			md.Bold("Members to Remove:") + "\n" + md.BulletList(items),
 67		))
 68	}
 69
 70	return b.String()
 71}
 72
 73func (p membersUpdatePropDefinition) Validate() (err error) {
 74	members := p.dao.Members()
 75
 76	p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr address) bool {
 77		if members.Has(addr) {
 78			err = errors.New("address is already a DAO member: " + addr.String())
 79			return true
 80		}
 81		return false
 82	})
 83
 84	if err != nil {
 85		return err
 86	}
 87
 88	p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr address) bool {
 89		if !members.Has(addr) {
 90			err = errors.New("address is not a DAO member: " + addr.String())
 91			return true
 92		}
 93		return false
 94	})
 95
 96	return err
 97}
 98
 99func (membersUpdatePropDefinition) Tally(ctx commondao.VotingContext) (bool, error) {
100	// When DAO has one or two members succeed when there is a YES vote, otherwise
101	// tally requires at least three votes to be able to tally by 2/3s super majority
102	if ctx.Members.Size() < 3 {
103		return ctx.VotingRecord.VoteCount(commondao.ChoiceYes) > 0, nil
104	}
105
106	if !commondao.IsQuorumReached(commondao.QuorumTwoThirds, ctx.VotingRecord, ctx.Members) {
107		return false, commondao.ErrNoQuorum
108	}
109
110	c, success := commondao.SelectChoiceBySuperMajority(ctx.VotingRecord, ctx.Members.Size())
111	if success {
112		return c == commondao.ChoiceYes, nil
113	}
114	return false, nil
115}
116
117func (p membersUpdatePropDefinition) Executor() commondao.ExecFunc {
118	return p.execute
119}
120
121func (p membersUpdatePropDefinition) execute(realm) error {
122	members := p.dao.Members()
123
124	p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr address) bool {
125		members.Add(addr)
126		return false
127	})
128
129	p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr address) bool {
130		members.Remove(addr)
131		return false
132	})
133
134	return nil
135}