Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

commondao.gno

4.23 Kb · 135 lines
  1package commondao
  2
  3import (
  4	"chain"
  5	"strconv"
  6
  7	"gno.land/p/moul/txlink/v0"
  8	"gno.land/p/nt/bptree/v0"
  9	"gno.land/p/nt/commondao/v0"
 10	"gno.land/p/nt/seqid/v0"
 11)
 12
 13// pkgPath is this realm's package path, fixed regardless of quarantine
 14// location. DAO treasury addresses derive from it, so they are stable
 15// across a SAME-PATH redeploy. Moving to a different package path (e.g.
 16// .../v1) changes cur.Sub derivation and would strand existing treasuries
 17// unless a drain executor ran first (see gnovm/adr/pr5890_realm_sub.md
 18// §Cross-host identity); this realm ships no such migration executor.
 19const pkgPath = "gno.land/r/nt/commondao/v0"
 20
 21// CommonDAOID is the ID of the realm's DAO.
 22const CommonDAOID uint64 = 1
 23
 24var (
 25	daoID     seqid.ID
 26	realmLink = txlink.Realm(pkgPath)
 27	daos      = bptree.NewBPTree32() // string(ID) -> *commondao.CommonDAO
 28	invites   = bptree.NewBPTree32() // string(address) -> address(inviter)
 29	creators  = bptree.NewBPTree32() // string(address) -> struct{}; consumed-invite marker
 30	listed    = bptree.NewBPTree32() // string(ID) -> uint64(ID); home-index opt-in
 31)
 32
 33func getDAO(daoID uint64) *commondao.CommonDAO {
 34	key := makeIDKey(daoID)
 35	if v := daos.Get(key); v != nil {
 36		return v.(*commondao.CommonDAO)
 37	}
 38	return nil
 39}
 40
 41func mustGetDAO(daoID uint64) *commondao.CommonDAO {
 42	dao := getDAO(daoID)
 43	if dao == nil {
 44		panic("DAO not found")
 45	}
 46	return dao
 47}
 48
 49func makeIDKey(daoID uint64) string {
 50	return seqid.ID(daoID).String()
 51}
 52
 53// isCreator reports whether an address has already consumed an invite and
 54// may create further DAOs without another one.
 55func isCreator(addr address) bool {
 56	return creators.Has(addr.String())
 57}
 58
 59// isListed reports whether a DAO opted into the realm's public home index.
 60func isListed(daoID uint64) bool {
 61	return listed.Has(makeIDKey(daoID))
 62}
 63
 64// setListed adds or removes a DAO from the realm's public home index.
 65func setListed(daoID uint64, v bool) {
 66	key := makeIDKey(daoID)
 67	if v {
 68		// Store the ID as the value so renderHome can paginate over the
 69		// listed set directly (keys are cford32-encoded, order-preserving).
 70		listed.Set(key, daoID)
 71	} else {
 72		listed.Remove(key)
 73	}
 74}
 75
 76// subpathOf returns the realm sub-identity subpath that acts for a DAO.
 77func subpathOf(daoID uint64) string {
 78	return "dao/" + strconv.FormatUint(daoID, 10)
 79}
 80
 81// daoAddress derives a DAO's treasury address: the address of the realm
 82// sub-identity cur.Sub(subpathOf(daoID)) that executors mint to move its
 83// funds. Pure derivation; DAO IDs are never reused, so the address is
 84// collision free and stable.
 85func daoAddress(daoID uint64) address {
 86	return chain.DerivePkgSubAddr(pkgPath, subpathOf(daoID))
 87}
 88
 89func createDAO(name, purpose, description string, members ...address) *commondao.CommonDAO {
 90	if len(members) == 0 {
 91		panic("a DAO requires at least one initial council member")
 92	}
 93
 94	id := daoID.Next()
 95	dao := commondao.New(newDAOOptions(uint64(id), name, purpose, description, nil, members)...)
 96	daos.Set(id.String(), dao)
 97	return dao
 98}
 99
100func createSubDAO(parent *commondao.CommonDAO, name, purpose, description string, members ...address) *commondao.CommonDAO {
101	if len(members) == 0 {
102		panic("a SubDAO requires at least one initial council member")
103	}
104
105	// WithParent also registers the new DAO as one of parent's children
106	id := daoID.Next()
107	dao := commondao.New(newDAOOptions(uint64(id), name, purpose, description, parent, members)...)
108	daos.Set(id.String(), dao)
109	return dao
110}
111
112func newDAOOptions(id uint64, name, purpose, description string, parent *commondao.CommonDAO, members []address) []commondao.Option {
113	opts := []commondao.Option{
114		commondao.WithID(id),
115		commondao.WithName(name),
116		commondao.WithPurpose(purpose),
117		commondao.WithDescription(description),
118		commondao.WithAddress(daoAddress(id)),
119	}
120	if parent != nil {
121		opts = append(opts, commondao.WithParent(parent))
122	}
123	for _, m := range members {
124		opts = append(opts, commondao.WithCouncilMember(m))
125	}
126
127	// Seed the default proposal kinds: every new DAO (genesis, user-created
128	// or sub-DAO) starts with these registered. The opt-in catalog kind
129	// (execution) is deliberately NOT seeded — a DAO gains it only after a
130	// supermajority CreateRegisterKindProposal.
131	for _, k := range defaultProposalKinds {
132		opts = append(opts, commondao.WithProposalKind(k))
133	}
134	return opts
135}