Skip to content

feat(commondao): add common proposal types to realm #4286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions examples/gno.land/r/nt/commondao/genesis.gno
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ func init() {

// Save DAO configuration
options.Set(id.String(), &Options{
AllowVoting: true,
AllowExecution: true,
AllowListing: true,
AllowRender: true,
AllowVoting: true,
AllowExecution: true,
AllowMembersUpdate: true,
})
}
40 changes: 40 additions & 0 deletions examples/gno.land/r/nt/commondao/options.gno
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,64 @@ type Options struct {

// AllowExecution enables proposal execution support.
AllowExecution bool

// AllowMembersUpdate allows creating a proposal to update DAO members.
AllowMembersUpdate bool

// AllowTextProposals allows creating general text proposals.
AllowTextProposals bool

// AllowSubDAOProposals allows creating SubDAOs though proposals.
AllowSubDAOProposals bool

// AllowDissolutionProposals allows dissolving DAOs though proposals.
AllowDissolutionProposals bool
}

// SetAllowListing toggles DAO public listing support.
func (o *Options) SetAllowListing(allow bool) {
o.AllowListing = allow
}

// SetAllowRender toggles DAO render support within the CommonDAO realm.
func (o *Options) SetAllowRender(allow bool) {
o.AllowRender = allow
}

// SetAllowChildren toggles DAO support for SubDAOs.
// Enabling child DAOs is necessary for hierarchical tree based DAOs.
func (o *Options) SetAllowChildren(allow bool) {
o.AllowChildren = allow
}

// SetAllowVoting toggles proposal voting support though the CommonDAO realm.
func (o *Options) SetAllowVoting(allow bool) {
o.AllowVoting = allow
}

// SetAllowExecution toggles proposal execution support though the CommonDAO realm.
func (o *Options) SetAllowExecution(allow bool) {
o.AllowExecution = allow
}

// SetAllowMembersUpdate toggles support for DAO members update proposals.
// This type of proposal allows adding or removing DAO members.
func (o *Options) SetAllowMembersUpdate(allow bool) {
o.AllowMembersUpdate = allow
}

// SetAllowTextProposals toggles support generic text proposals.
func (o *Options) SetAllowTextProposals(allow bool) {
o.AllowTextProposals = allow
}

// SetAllowSubDAOPorposals toggles support for SubDAO creation proposals.
// SubDAOs can only be created when DAO children support is enabled.
func (o *Options) SetAllowSubDAOPorposals(allow bool) {
o.AllowSubDAOProposals = allow
}

// SetAllowDissolutionProposals toggles support for DAO dissolution proposals.
func (o *Options) SetAllowDissolutionProposals(allow bool) {
o.AllowDissolutionProposals = allow
}
134 changes: 134 additions & 0 deletions examples/gno.land/r/nt/commondao/proposal_members.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package commondao

import (
"errors"
"std"
"strings"
"time"

"gno.land/p/moul/addrset"
"gno.land/p/moul/md"
"gno.land/p/nt/commondao"
)

// newMembersPropDefinition creates a new proposal definition for adding/removing DAO members.
func newMembersPropDefinition(dao *commondao.CommonDAO, add, remove addrset.Set) membersPropDefinition {
if dao == nil {
panic("DAO is required")
}

if dao.Members().Size() == 0 {
panic("a DAO with at least one member is required to create member update proposals")
}

if add.Size() == 0 && remove.Size() == 0 {
panic("no members were specified to be added or removed")
}

return membersPropDefinition{
dao: dao,
toAdd: add,
toRemove: remove,
}
}

// nembersUpdateProposal defines a proposal type for adding/removing DAO members.
type membersPropDefinition struct {
dao *commondao.CommonDAO
toAdd, toRemove addrset.Set
}

func (membersPropDefinition) Title() string { return "Members Update" }
func (membersPropDefinition) VotingPeriod() time.Duration { return time.Hour * 24 * 7 }

func (p membersPropDefinition) Body() string {
var b strings.Builder

if p.toAdd.Size() > 0 {
items := make([]string, 0, p.toAdd.Size())
p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr std.Address) bool {
items = append(items, addr.String())
return false
})

b.WriteString(md.Paragraph(
md.Bold("Members to Add:") + "\n" + md.BulletList(items),
))
}

if p.toRemove.Size() > 0 {
items := make([]string, 0, p.toRemove.Size())
p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr std.Address) bool {
items = append(items, addr.String())
return false
})

b.WriteString(md.Paragraph(
md.Bold("Members to Remove:") + "\n" + md.BulletList(items),
))
}

return b.String()
}

func (p membersPropDefinition) Validate() (err error) {
members := p.dao.Members()

p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr std.Address) bool {
if members.Has(addr) {
err = errors.New("address is already a DAO member: " + addr.String())
return true
}
return false
})

if err != nil {
return err
}

p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr std.Address) bool {
if !members.Has(addr) {
err = errors.New("address is not a DAO member: " + addr.String())
return true
}
return false
})

return err
}

func (membersPropDefinition) Tally(r commondao.ReadonlyVotingRecord, members commondao.MemberSet) (bool, error) {
// When DAO has one or two members succeed when there is a YES vote, otherwise
// tally requires at least three votes to be able to tally by 2/3s super majority
if members.Size() < 3 {
return r.VoteCount(commondao.ChoiceYes) > 0, nil
}

if !commondao.IsQuorumReached(commondao.QuorumTwoThirds, r, members) {
return false, commondao.ErrNoQuorum
}

c, success := commondao.SelectChoiceBySuperMajority(r, members.Size())
if success {
return c == commondao.ChoiceYes, nil
}
return false, nil
}

func (p membersPropDefinition) Execute() error {
crossing()

members := p.dao.Members()

p.toAdd.IterateByOffset(0, p.toAdd.Size(), func(addr std.Address) bool {
members.Add(addr)
return false
})

p.toRemove.IterateByOffset(0, p.toRemove.Size(), func(addr std.Address) bool {
members.Remove(addr)
return false
})

return nil
}
Loading
Loading