proposal_status.gno
7.45 Kb · 244 lines
1package governance
2
3import (
4 gnsmath "gno.land/p/gnoswap/gnsmath"
5
6 "gno.land/r/gnoswap/gov/governance"
7)
8
9type ProposalStatusResolver struct {
10 *governance.ProposalStatus
11 scheduleResolver *ProposalScheduleStatusResolver
12 actionStatusResolver *ProposalActionStatusResolver
13 voteStatusResolver *ProposalVoteStatusResolver
14}
15
16func NewProposalStatusResolver(status *governance.ProposalStatus) *ProposalStatusResolver {
17 return &ProposalStatusResolver{
18 ProposalStatus: status,
19 scheduleResolver: NewProposalScheduleStatusResolver(status.Schedule()),
20 actionStatusResolver: NewProposalActionStatusResolver(status.ActionStatus()),
21 voteStatusResolver: NewProposalVoteStatusResolver(status.VoteStatus()),
22 }
23}
24
25// TotalVoteWeight returns the total weight of all votes cast.
26//
27// Returns:
28// - int64: total vote weight
29func (p *ProposalStatusResolver) TotalVoteWeight() int64 {
30 return p.voteStatusResolver.TotalVoteWeight()
31}
32
33// StatusType determines the current status of the proposal based on timing, voting, and actions.
34// This is the main status calculation method that considers all factors.
35//
36// Parameters:
37// - current: current timestamp to evaluate status at
38//
39// Returns:
40// - ProposalStatusType: current status of the proposal
41func (p *ProposalStatusResolver) StatusType(current int64) governance.ProposalStatusType {
42 actionStatus := p.ActionStatus()
43
44 // Check action-based statuses first (these override time-based statuses)
45 if actionStatus.IsExecuted() {
46 return governance.StatusExecuted
47 }
48
49 if actionStatus.Canceled() {
50 return governance.StatusCanceled
51 }
52
53 // Check time-based statuses
54 if !p.scheduleResolver.IsPassedActiveAt(current) {
55 return governance.StatusUpcoming
56 }
57
58 if !p.scheduleResolver.IsPassedVotingEndedAt(current) {
59 return governance.StatusActive
60 }
61
62 // Check voting outcome after voting period ends
63 if !p.voteStatusResolver.IsPassed() {
64 return governance.StatusRejected
65 }
66
67 // For passed proposals, check execution status
68 if !p.ActionStatus().IsExecutable() || !p.scheduleResolver.IsPassedExecutableAt(current) {
69 return governance.StatusPassed
70 }
71
72 if !p.scheduleResolver.IsPassedExpiredAt(current) {
73 return governance.StatusExecutable
74 }
75
76 return governance.StatusExpired
77}
78
79// IsUpcoming checks if the proposal is in upcoming status.
80//
81// Parameters:
82// - current: timestamp to check status at
83//
84// Returns:
85// - bool: true if proposal is upcoming
86func (p *ProposalStatusResolver) IsUpcoming(current int64) bool {
87 return p.StatusType(current) == governance.StatusUpcoming
88}
89
90// IsActive checks if the proposal is in active voting status.
91//
92// Parameters:
93// - current: timestamp to check status at
94//
95// Returns:
96// - bool: true if proposal is active (voting period)
97func (p *ProposalStatusResolver) IsActive(current int64) bool {
98 return p.StatusType(current) == governance.StatusActive
99}
100
101// IsPassed checks if the proposal has passed voting.
102//
103// Parameters:
104// - current: timestamp to check status at
105//
106// Returns:
107// - bool: true if proposal has passed
108func (p *ProposalStatusResolver) IsPassed(current int64) bool {
109 return p.StatusType(current) == governance.StatusPassed
110}
111
112// IsRejected checks if the proposal has been rejected by voting.
113//
114// Parameters:
115// - current: timestamp to check status at
116//
117// Returns:
118// - bool: true if proposal was rejected
119func (p *ProposalStatusResolver) IsRejected(current int64) bool {
120 return p.StatusType(current) == governance.StatusRejected
121}
122
123// IsExecutable checks if the proposal is in executable status.
124//
125// Parameters:
126// - current: timestamp to check status at
127//
128// Returns:
129// - bool: true if proposal can be executed
130func (p *ProposalStatusResolver) IsExecutable(current int64) bool {
131 return p.StatusType(current) == governance.StatusExecutable
132}
133
134// IsExpired checks if the proposal execution window has expired.
135//
136// Parameters:
137// - current: timestamp to check status at
138//
139// Returns:
140// - bool: true if proposal has expired
141func (p *ProposalStatusResolver) IsExpired(current int64) bool {
142 return p.StatusType(current) == governance.StatusExpired
143}
144
145// IsExecuted checks if the proposal has been executed.
146//
147// Parameters:
148// - current: timestamp to check status at
149//
150// Returns:
151// - bool: true if proposal has been executed
152func (p *ProposalStatusResolver) IsExecuted(current int64) bool {
153 return p.StatusType(current) == governance.StatusExecuted
154}
155
156// IsCanceled checks if the proposal has been canceled.
157//
158// Parameters:
159// - current: timestamp to check status at
160//
161// Returns:
162// - bool: true if proposal has been canceled
163func (p *ProposalStatusResolver) IsCanceled(current int64) bool {
164 return p.StatusType(current) == governance.StatusCanceled
165}
166
167// cancel marks the proposal as canceled with the provided details.
168// This delegates to the action status for actual cancellation logic.
169//
170// Parameters:
171// - canceledAt: timestamp when proposal was canceled
172// - canceledHeight: block height when proposal was canceled
173// - canceledBy: address that canceled the proposal
174//
175// Returns:
176// - error: cancellation error if operation fails
177func (p *ProposalStatusResolver) cancel(canceledAt int64, canceledHeight int64, canceledBy address) error {
178 return p.actionStatusResolver.cancel(canceledAt, canceledHeight, canceledBy)
179}
180
181// execute marks the proposal as executed with the provided details.
182// This delegates to the action status for actual execution logic.
183//
184// Parameters:
185// - executedAt: timestamp when proposal was executed
186// - executedHeight: block height when proposal was executed
187// - executedBy: address that executed the proposal
188//
189// Returns:
190// - error: execution error if operation fails
191func (p *ProposalStatusResolver) execute(executedAt int64, executedHeight int64, executedBy address) error {
192 return p.actionStatusResolver.Execute(executedAt, executedHeight, executedBy)
193}
194
195// vote records a vote on the proposal and updates vote tallies.
196// This delegates to the vote status for actual vote recording.
197//
198// Parameters:
199// - votedYes: true for "yes" vote, false for "no" vote
200// - weight: voting weight to apply
201//
202// Returns:
203// - error: voting error if operation fails
204func (p *ProposalStatusResolver) vote(votedYes bool, weight int64) error {
205 if votedYes {
206 return p.voteStatusResolver.AddYesVoteWeight(weight)
207 }
208
209 return p.voteStatusResolver.AddNoVoteWeight(weight)
210}
211
212// NewProposalStatus creates a new proposal status with the specified configuration.
213// This initializes all status components with the governance configuration and timing.
214//
215// Parameters:
216// - config: governance configuration to use
217// - maxVotingWeight: maximum voting weight for this proposal
218// - executable: whether this proposal type can be executed
219// - createdAt: timestamp when proposal was created
220//
221// Returns:
222// - *ProposalStatus: new proposal status instance
223func NewProposalStatus(
224 config governance.Config,
225 maxVotingWeight int64,
226 executable bool,
227 createdAt int64,
228 quorumWeight int64,
229) *governance.ProposalStatus {
230 schedule := NewProposalScheduleStatus(
231 config.VotingStartDelay,
232 config.VotingPeriod,
233 config.ExecutionDelay,
234 config.ExecutionWindow,
235 createdAt,
236 )
237 actionStatus := governance.NewProposalActionStatus(executable)
238
239 // Calculate the quorum amount from the current xGNS supply, not the smoothed voting weight.
240 quorumAmount := gnsmath.SafeMulDivInt64(quorumWeight, config.Quorum, 100)
241 voteStatus := governance.NewProposalVoteStatus(maxVotingWeight, quorumAmount)
242
243 return governance.NewProposalStatusBy(schedule, actionStatus, voteStatus)
244}