Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

proposal.gno

6.38 Kb · 205 lines
  1package governance
  2
  3import (
  4	"errors"
  5
  6	"gno.land/r/gnoswap/gov/governance"
  7)
  8
  9type ProposalResolver struct {
 10	*governance.Proposal
 11	statusResolver   *ProposalStatusResolver
 12	dataResolver     *ProposalDataResolver
 13	metadataResolver *ProposalMetadataResolver
 14}
 15
 16// NewProposalResolver wraps a persisted governance proposal and initializes
 17// resolvers for its status, type-specific data, and metadata.
 18//
 19// Parameters:
 20//   - proposal: persisted proposal to resolve
 21//
 22// Returns:
 23//   - *ProposalResolver: resolver backed by proposal and its nested state
 24func NewProposalResolver(proposal *governance.Proposal) *ProposalResolver {
 25	statusResolver := NewProposalStatusResolver(proposal.Status())
 26	dataResolver := NewProposalDataResolver(proposal.Data())
 27	metadataResolver := NewProposalMetadataResolver(proposal.Metadata())
 28	return &ProposalResolver{
 29		Proposal:         proposal,
 30		statusResolver:   statusResolver,
 31		dataResolver:     dataResolver,
 32		metadataResolver: metadataResolver,
 33	}
 34}
 35
 36// VotingTotalWeight returns the total weight of all votes cast by combining
 37// the proposal's yes and no tallies.
 38//
 39// Returns:
 40//   - int64: combined weight of all recorded votes
 41func (r *ProposalResolver) VotingTotalWeight() int64 {
 42	return r.statusResolver.TotalVoteWeight()
 43}
 44
 45// IsActive determines whether the proposal is active at current.
 46// Upcoming, voting, and passed executable proposals are active; rejected,
 47// expired, executed, canceled, and passed text proposals are inactive.
 48//
 49// Parameters:
 50//   - current: timestamp at which the proposal status is evaluated
 51//
 52// Returns:
 53//   - bool: true when the proposal can still be voted on or executed at current
 54func (r *ProposalResolver) IsActive(current int64) bool {
 55	// Calculate status once and reuse to avoid redundant computations
 56	status := r.statusResolver.StatusType(current)
 57
 58	switch status {
 59	case governance.StatusRejected,
 60		governance.StatusExpired,
 61		governance.StatusExecuted,
 62		governance.StatusCanceled:
 63		return false
 64	case governance.StatusPassed:
 65		// Text proposals become inactive once they pass (no execution needed)
 66		return !r.IsTextType()
 67	default:
 68		// StatusUpcoming, StatusActive, StatusExecutable are considered active
 69		return true
 70	}
 71}
 72
 73// Validate performs comprehensive validation of the proposal's type-specific
 74// data and metadata before the proposal is stored.
 75//
 76// Returns:
 77//   - error: nil when both data and metadata validate; the first validation error otherwise
 78func (r *ProposalResolver) Validate() error {
 79	// Validate type-specific proposal data
 80	if err := r.dataResolver.Validate(); err != nil {
 81		return err
 82	}
 83
 84	// Validate proposal metadata (title and description)
 85	if err := r.metadataResolver.Validate(); err != nil {
 86		return err
 87	}
 88
 89	return nil
 90}
 91
 92// Status returns the current status string of the proposal at current.
 93//
 94// Parameters:
 95//   - current: timestamp at which the proposal status is evaluated
 96//
 97// Returns:
 98//   - string: human-readable status name for the proposal at current
 99func (r *ProposalResolver) Status(current int64) string {
100	return r.statusResolver.StatusType(current).String()
101}
102
103// StatusType returns the current status type of the proposal at current.
104//
105// Parameters:
106//   - current: timestamp at which the proposal status is evaluated
107//
108// Returns:
109//   - governance.ProposalStatusType: lifecycle status at current
110func (r *ProposalResolver) StatusType(current int64) governance.ProposalStatusType {
111	return r.statusResolver.StatusType(current)
112}
113
114// IsVotingPeriod reports whether votedAt falls within the proposal's active
115// voting status.
116//
117// Parameters:
118//   - votedAt: timestamp at which voting eligibility is checked
119//
120// Returns:
121//   - bool: true when the proposal status at votedAt is StatusActive
122func (r *ProposalResolver) IsVotingPeriod(votedAt int64) bool {
123	return r.StatusType(votedAt) == governance.StatusActive
124}
125
126// IsExecutable determines whether the proposal can be executed at current.
127// The proposal type must support execution and its status must be executable.
128//
129// Parameters:
130//   - current: timestamp at which execution eligibility is evaluated
131//
132// Returns:
133//   - bool: true when this type and current lifecycle state permit execution
134func (r *ProposalResolver) IsExecutable(current int64) bool {
135	// Only certain proposal types can be executed
136	if !r.dataResolver.ProposalType().IsExecutable() {
137		return false
138	}
139
140	return r.statusResolver.IsExecutable(current)
141}
142
143// CommunityPoolSpendTokenPath returns the token path for a community pool spend proposal.
144// It returns an empty string when the proposal has no community-pool spend data.
145//
146// Returns:
147//   - string: token package path to spend, or empty string for other proposal types
148func (r *ProposalResolver) CommunityPoolSpendTokenPath() string {
149	if r.Data() == nil {
150		return ""
151	}
152
153	communityPoolSpend := r.dataResolver.CommunityPoolSpend()
154	if communityPoolSpend == nil {
155		return ""
156	}
157
158	return communityPoolSpend.TokenPath()
159}
160
161// Vote records a vote for this proposal and updates its yes or no tally.
162// This is an internal method called during the voting process.
163//
164// Parameters:
165//   - votedYes: true to add weight to the yes tally; false to add it to no
166//   - weight: voting weight to add to the selected tally
167//
168// Returns:
169//   - error: nil when the selected tally is updated; an error from vote-status validation otherwise
170func (r *ProposalResolver) Vote(votedYes bool, weight int64) error {
171	return r.statusResolver.vote(votedYes, weight)
172}
173
174// execute marks the proposal as executed and records execution details.
175// This method validates execution conditions before proceeding.
176func (r *ProposalResolver) execute(
177	executedAt, executedHeight int64,
178	executedBy address,
179) error {
180	// Verify proposal is in executable state
181	if !r.IsExecutable(executedAt) {
182		return errors.New(errProposalNotExecutable)
183	}
184
185	// Mark proposal as executed
186	return r.statusResolver.execute(executedAt, executedHeight, executedBy)
187}
188
189// cancel marks the proposal as canceled and records cancellation details.
190// This method validates cancellation conditions before proceeding.
191func (r *ProposalResolver) cancel(
192	canceledAt, canceledHeight int64,
193	canceledBy address,
194) error {
195	if r.statusResolver.IsCanceled(canceledAt) {
196		return errors.New(errAlreadyCanceledProposal)
197	}
198
199	if !r.statusResolver.IsUpcoming(canceledAt) {
200		return errors.New(errUnableToCancelVotingProposal)
201	}
202
203	// Mark proposal as canceled
204	return r.statusResolver.cancel(canceledAt, canceledHeight, canceledBy)
205}