daokit.gno
3.07 Kb · 107 lines
1package daokit
2
3import (
4 "time"
5
6 "gno.land/p/samcrew/daocond"
7)
8
9type DAO interface {
10 // Creates a new proposal and returns its ID.
11 Propose(req ProposalRequest) uint64
12 // Casts a vote on a specific proposal.
13 Vote(id uint64, vote daocond.Vote)
14 // Executes a proposal if it meets the required conditions.
15 // The rlm realm is threaded so action handlers can perform
16 // cross-realm calls (e.g. editing the DAO profile).
17 Execute(id uint64, rlm realm)
18
19 // Generates a web interface representation for the given path.
20 Render(path string) string
21
22 // Gets an extension by path, returns nil if not found.
23 Extension(path string) Extension
24 // Lists all available extensions.
25 ExtensionsList() ExtensionsList
26}
27
28// Function type for updating DAO implementation during governance upgrades.
29type SetImplemFn = func(implem DAO)
30
31// Creates, votes yes, and immediately executes a proposal in one operation.
32// Useful when you have enough permission to execute an action directly.
33// Examples: migrations, adding members
34func InstantExecute(d DAO, req ProposalRequest, rlm realm) uint64 {
35 id := d.Propose(req)
36 d.Vote(id, daocond.VoteYes)
37 d.Execute(id, rlm)
38 return id
39}
40
41// Manages the essential components of a DAO: resources, proposals, and extensions.
42type Core struct {
43 Resources *ResourcesStore // Available actions and their conditions
44 Proposals *ProposalsStore // All proposals and their voting state
45 Extensions *ExtensionsStore // Pluggable functionality modules
46}
47
48// Creates a new DAO core with empty stores.
49func NewCore() *Core {
50 return &Core{
51 Resources: NewResourcesStore(),
52 Proposals: NewProposalsStore(),
53 Extensions: &ExtensionsStore{},
54 }
55}
56
57// Records a vote for a specific proposal from a voter.
58func (d *Core) Vote(voterID string, proposalID uint64, vote daocond.Vote) {
59 proposal := d.Proposals.GetProposal(proposalID)
60 if proposal == nil {
61 panic("proposal not found")
62 }
63
64 if proposal.Status != ProposalStatusOpen {
65 panic("proposal is not open")
66 }
67
68 proposal.Ballot.Vote(voterID, daocond.Vote(vote))
69}
70
71// Executes a proposal if it meets the required voting conditions.
72func (d *Core) Execute(proposalID uint64, rlm realm) {
73 proposal := d.Proposals.GetProposal(proposalID)
74 if proposal == nil {
75 panic("proposal not found")
76 }
77
78 if proposal.Status != ProposalStatusOpen {
79 panic("proposal is not open")
80 }
81
82 if !proposal.Condition.Eval(proposal.Ballot) {
83 panic("proposal condition is not met")
84 }
85
86 proposal.UpdateStatus()
87 if proposal.Status != ProposalStatusPassed {
88 panic("proposal does not meet the condition(s) or is already closed/executed")
89 }
90
91 d.Resources.Get(proposal.Action.Type()).Handler.Execute(proposal.Action, rlm)
92 proposal.Status = ProposalStatusExecuted
93 proposal.ExecutedAt = time.Now()
94}
95
96// Creates a new proposal for voting and returns its ID.
97func (d *Core) Propose(proposerID string, req ProposalRequest) uint64 {
98 actionType := req.Action.Type()
99
100 resource := d.Resources.Get(actionType)
101 if resource == nil {
102 panic("action type is not registered as a resource")
103 }
104
105 prop := d.Proposals.newProposal(proposerID, req, resource.Condition)
106 return uint64(prop.ID)
107}