instance.gno
4.87 Kb · 179 lines
1package v1
2
3import (
4 bptree "gno.land/p/nt/bptree/v0"
5 ufmt "gno.land/p/nt/ufmt/v0"
6 "gno.land/r/gnoswap/gov/governance"
7)
8
9type governanceV1 struct {
10 store governance.IGovernanceStore
11 stakerAccessor governance.GovStakerAccessor
12}
13
14func NewGovernanceV1(
15 governanceStore governance.IGovernanceStore,
16 stakerAccessor governance.GovStakerAccessor,
17) governance.IGovernance {
18 return &governanceV1{
19 store: governanceStore,
20 stakerAccessor: stakerAccessor,
21 }
22}
23
24// Config version methods
25func (g *governanceV1) getCurrentConfigVersion() int64 {
26 return g.store.GetConfigCounter().Get()
27}
28
29// nextConfigVersion increments and persists the config counter. It mutates the
30// underlying KV store, so it requires a realm value that resolves to the
31// governance proxy realm (the only writer-permitted address).
32func (g *governanceV1) nextConfigVersion(_ int, rlm realm) int64 {
33 counter := g.store.GetConfigCounter()
34 next := counter.Next()
35 if err := g.store.SetConfigCounter(0, rlm, counter); err != nil {
36 panic(err)
37 }
38 return next
39}
40
41// Proposal ID methods
42func (g *governanceV1) getCurrentProposalID() int64 {
43 return g.store.GetProposalCounter().Get()
44}
45
46// nextProposalID increments and persists the proposal counter. Same realm
47// rules as nextConfigVersion.
48func (g *governanceV1) nextProposalID(_ int, rlm realm) int64 {
49 counter := g.store.GetProposalCounter()
50 next := counter.Next()
51 if err := g.store.SetProposalCounter(0, rlm, counter); err != nil {
52 panic(err)
53 }
54 return next
55}
56
57// Config methods
58func (g *governanceV1) getConfig(version int64) (governance.Config, bool) {
59 return g.store.GetConfig(version)
60}
61
62func (g *governanceV1) setConfig(_ int, rlm realm, version int64, config governance.Config) error {
63 return g.store.SetConfig(0, rlm, version, config)
64}
65
66func (g *governanceV1) getCurrentConfig() (governance.Config, bool) {
67 return g.getConfig(g.getCurrentConfigVersion())
68}
69
70// Proposal methods
71func (g *governanceV1) getProposal(id int64) (*governance.Proposal, bool) {
72 proposal, exists := g.store.GetProposal(id)
73 if !exists {
74 return nil, false
75 }
76
77 return proposal, true
78}
79
80func (g *governanceV1) addProposal(_ int, rlm realm, proposal *governance.Proposal) bool {
81 // Set the proposal (ID already set in proposal)
82 err := g.store.SetProposal(0, rlm, proposal.ID(), proposal)
83 if err != nil {
84 return false
85 }
86
87 // Add to user proposals
88 err = g.store.AddUserProposal(0, rlm, proposal.Proposer().String(), proposal.ID())
89 if err != nil {
90 return false
91 }
92
93 return true
94}
95
96// User proposals methods
97func (g *governanceV1) getUserProposals(user string) []*governance.Proposal {
98 proposalIDs, exists := g.store.GetUserProposalIDs(user)
99 if !exists {
100 return []*governance.Proposal{}
101 }
102
103 proposals := make([]*governance.Proposal, 0)
104
105 for _, id := range proposalIDs {
106 proposal, exists := g.store.GetProposal(id)
107 if !exists {
108 continue
109 }
110
111 proposals = append(proposals, proposal)
112 }
113
114 return proposals
115}
116
117// hasActiveProposal assumes removeInactiveUserProposals has already pruned stale proposals.
118func (g *governanceV1) hasActiveProposal(proposerAddress address) bool {
119 proposals := g.getUserProposals(proposerAddress.String())
120
121 return len(proposals) > 0
122}
123
124// Remove inactive user proposals
125// This function is used to remove inactive proposals from the user proposals list.
126// It is used to clean up user's active proposal list when creating a new proposal.
127func (g *governanceV1) removeInactiveUserProposals(_ int, rlm realm, proposerAddress address, current int64) error {
128 proposals := g.getUserProposals(proposerAddress.String())
129
130 for _, proposal := range proposals {
131 proposalResolver := NewProposalResolver(proposal)
132
133 if !proposalResolver.IsActive(current) {
134 err := g.store.RemoveUserProposal(0, rlm, proposerAddress.String(), proposal.ID())
135 if err != nil {
136 return err
137 }
138 }
139 }
140
141 return nil
142}
143
144// Proposal voting info methods
145func (g *governanceV1) getProposalUserVotingInfos(proposalID int64) (*bptree.BPTree, bool) {
146 return g.store.GetProposalVotingInfos(proposalID)
147}
148
149func (g *governanceV1) updateProposalUserVotes(_ int, rlm realm, proposal *governance.Proposal, userVotingInfos *bptree.BPTree) error {
150 return g.store.SetProposalVotingInfos(0, rlm, proposal.ID(), userVotingInfos)
151}
152
153// Helper methods for API
154func (g *governanceV1) mustGetProposal(id int64) *governance.Proposal {
155 proposal, exists := g.getProposal(id)
156 if !exists {
157 panic(makeErrorWithDetails(errProposalNotFound, ufmt.Sprintf("proposal(%d) not found", id)))
158 }
159 return proposal
160}
161
162func (g *governanceV1) getProposalUserVotingInfo(proposalID int64, addr address) (*governance.VotingInfo, bool) {
163 votingInfosTree, exists := g.getProposalUserVotingInfos(proposalID)
164 if !exists {
165 return nil, false
166 }
167
168 votingInfoRaw, exists := votingInfosTree.Get(addr.String())
169 if !exists {
170 return nil, false
171 }
172
173 votingInfo, ok := votingInfoRaw.(*governance.VotingInfo)
174 if !ok {
175 return nil, false
176 }
177
178 return votingInfo, true
179}