Search Apps Documentation Source Content File Folder Download Copy Actions Download

proposals.gno

2.88 Kb ยท 126 lines
  1package daokit
  2
  3import (
  4	"chain/runtime"
  5	"errors"
  6	"time"
  7
  8	"gno.land/p/nt/avl"
  9	"gno.land/p/nt/seqid"
 10	"gno.land/p/onbloc/json"
 11	"gno.land/p/samcrew/daocond"
 12)
 13
 14type ProposalStatus int
 15
 16const (
 17	ProposalStatusOpen ProposalStatus = iota
 18	ProposalStatusPassed
 19	ProposalStatusExecuted
 20)
 21
 22func (s ProposalStatus) String() string {
 23	switch s {
 24	case ProposalStatusOpen:
 25		return "Open"
 26	case ProposalStatusPassed:
 27		return "Passed"
 28	case ProposalStatusExecuted:
 29		return "Executed"
 30	default:
 31		return "Unknown"
 32	}
 33}
 34
 35type Proposal struct {
 36	ID            seqid.ID
 37	Title         string
 38	Description   string
 39	CreatedAt     time.Time
 40	CreatedHeight int64
 41	ProposerID    string
 42	Condition     daocond.Condition
 43	Action        Action
 44	Status        ProposalStatus
 45	ExecutorID    string
 46	ExecutedAt    time.Time
 47	Ballot        daocond.Ballot
 48}
 49
 50type ProposalsStore struct {
 51	Tree  *avl.Tree // int -> Proposal
 52	genID seqid.ID
 53}
 54
 55type ProposalRequest struct {
 56	Title       string
 57	Description string
 58	Action      Action
 59}
 60
 61func NewProposalsStore() *ProposalsStore {
 62	return &ProposalsStore{
 63		Tree: avl.NewTree(),
 64	}
 65}
 66
 67func (p *ProposalsStore) newProposal(proposer string, req ProposalRequest, condition daocond.Condition) *Proposal {
 68	id := p.genID.Next()
 69	proposal := &Proposal{
 70		ID:            id,
 71		Title:         req.Title,
 72		Description:   req.Description,
 73		ProposerID:    proposer,
 74		Status:        ProposalStatusOpen,
 75		Action:        req.Action,
 76		Condition:     condition,
 77		Ballot:        daocond.NewBallot(),
 78		CreatedAt:     time.Now(),
 79		CreatedHeight: runtime.ChainHeight(),
 80	}
 81	p.Tree.Set(id.String(), proposal)
 82	return proposal
 83}
 84
 85func (p *ProposalsStore) GetProposal(id uint64) *Proposal {
 86	value, ok := p.Tree.Get(seqid.ID(id).String())
 87	if !ok {
 88		return nil
 89	}
 90	proposal := value.(*Proposal)
 91	return proposal
 92}
 93
 94func (p *Proposal) UpdateStatus() {
 95	conditionsAreMet := p.Condition.Eval(p.Ballot)
 96	if p.Status == ProposalStatusOpen && conditionsAreMet {
 97		p.Status = ProposalStatusPassed
 98	}
 99}
100
101func (p *ProposalsStore) GetProposalsJSON() string {
102	props := make([]*json.Node, 0, p.Tree.Size())
103	// XXX: pagination
104	p.Tree.Iterate("", "", func(key string, value interface{}) bool {
105		prop, ok := value.(*Proposal)
106		if !ok {
107			panic(errors.New("unexpected invalid proposal type"))
108		}
109		prop.UpdateStatus()
110		props = append(props, json.ObjectNode("", map[string]*json.Node{
111			"id":          json.NumberNode("", float64(prop.ID)),
112			"title":       json.StringNode("", prop.Title),
113			"description": json.StringNode("", prop.Description),
114			"proposer":    json.StringNode("", prop.ProposerID),
115			"status":      json.StringNode("", prop.Status.String()),
116			"startHeight": json.NumberNode("", float64(prop.CreatedHeight)),
117			"signal":      json.NumberNode("", prop.Condition.Signal(prop.Ballot)),
118		}))
119		return false
120	})
121	bz, err := json.Marshal(json.ArrayNode("", props))
122	if err != nil {
123		panic(err)
124	}
125	return string(bz)
126}