proposal.gno
4.54 Kb ยท 146 lines
1package v1
2
3import (
4 "gno.land/r/gnoswap/gov/governance"
5)
6
7type ProposalResolver struct {
8 *governance.Proposal
9 statusResolver *ProposalStatusResolver
10 dataResolver *ProposalDataResolver
11 metadataResolver *ProposalMetadataResolver
12}
13
14func NewProposalResolver(proposal *governance.Proposal) *ProposalResolver {
15 statusResolver := NewProposalStatusResolver(proposal.Status())
16 dataResolver := NewProposalDataResolver(proposal.Data())
17 metadataResolver := NewProposalMetadataResolver(proposal.Metadata())
18 return &ProposalResolver{
19 Proposal: proposal,
20 statusResolver: statusResolver,
21 dataResolver: dataResolver,
22 metadataResolver: metadataResolver,
23 }
24}
25
26// VotingTotalWeight returns total weight of all votes cast.
27func (r *ProposalResolver) VotingTotalWeight() int64 {
28 return r.statusResolver.TotalVoteWeight()
29}
30
31// IsActive determines if the proposal is currently active (can be voted on or executed).
32// A proposal is considered active if it's not rejected, expired, executed, or canceled.
33func (r *ProposalResolver) IsActive(current int64) bool {
34 // Calculate status once and reuse to avoid redundant computations
35 status := r.statusResolver.StatusType(current)
36
37 switch status {
38 case governance.StatusRejected,
39 governance.StatusExpired,
40 governance.StatusExecuted,
41 governance.StatusCanceled:
42 return false
43 case governance.StatusPassed:
44 // Text proposals become inactive once they pass (no execution needed)
45 return !r.IsTextType()
46 default:
47 // StatusUpcoming, StatusActive, StatusExecutable are considered active
48 return true
49 }
50}
51
52// Validate performs comprehensive validation of the proposal data and metadata.
53// This ensures all proposal components meet requirements before storage.
54func (r *ProposalResolver) Validate() error {
55 // Validate type-specific proposal data
56 if err := r.dataResolver.Validate(); err != nil {
57 return err
58 }
59
60 // Validate proposal metadata (title and description)
61 if err := r.metadataResolver.Validate(); err != nil {
62 return err
63 }
64
65 return nil
66}
67
68// Status returns the current status string of the proposal at the given time.
69func (r *ProposalResolver) Status(current int64) string {
70 return r.statusResolver.StatusType(current).String()
71}
72
73// StatusType returns the current status type of the proposal at the given time.
74func (r *ProposalResolver) StatusType(current int64) governance.ProposalStatusType {
75 return r.statusResolver.StatusType(current)
76}
77
78// IsVotingPeriod checks if the proposal is currently in its voting period.
79func (r *ProposalResolver) IsVotingPeriod(votedAt int64) bool {
80 return r.StatusType(votedAt) == governance.StatusActive
81}
82
83// IsExecutable determines if the proposal can be executed at the given time.
84// Only executable proposal types that have passed voting can be executed.
85func (r *ProposalResolver) IsExecutable(current int64) bool {
86 // Only certain proposal types can be executed
87 if !r.dataResolver.ProposalType().IsExecutable() {
88 return false
89 }
90
91 return r.statusResolver.IsExecutable(current)
92}
93
94// CommunityPoolSpendTokenPath returns the token path for community pool spend proposals.
95// Returns empty string for other proposal types.
96func (r *ProposalResolver) CommunityPoolSpendTokenPath() string {
97 if r.Data() == nil {
98 return ""
99 }
100
101 communityPoolSpend := r.dataResolver.CommunityPoolSpend()
102 if communityPoolSpend == nil {
103 return ""
104 }
105
106 return communityPoolSpend.TokenPath()
107}
108
109// vote records a vote for this proposal and updates vote tallies.
110// This is an internal method called during voting process.
111func (r *ProposalResolver) Vote(votedYes bool, weight int64) error {
112 return r.statusResolver.vote(votedYes, weight)
113}
114
115// execute marks the proposal as executed and records execution details.
116// This method validates execution conditions before proceeding.
117func (r *ProposalResolver) execute(
118 executedAt, executedHeight int64,
119 executedBy address,
120) error {
121 // Verify proposal is in executable state
122 if !r.IsExecutable(executedAt) {
123 return errProposalNotExecutable
124 }
125
126 // Mark proposal as executed
127 return r.statusResolver.execute(executedAt, executedHeight, executedBy)
128}
129
130// cancel marks the proposal as canceled and records cancellation details.
131// This method validates cancellation conditions before proceeding.
132func (r *ProposalResolver) cancel(
133 canceledAt, canceledHeight int64,
134 canceledBy address,
135) error {
136 if r.statusResolver.IsCanceled(canceledAt) {
137 return errAlreadyCanceledProposal
138 }
139
140 if !r.statusResolver.IsUpcoming(canceledAt) {
141 return errUnableToCancelVotingProposal
142 }
143
144 // Mark proposal as canceled
145 return r.statusResolver.cancel(canceledAt, canceledHeight, canceledBy)
146}