Kind Realm (renderable)
Name v1
Namespace gnoswap / gov / governance
Exported functions n/a — Gnoland RPC unavailable (functions): rpc abci_query: HTTP 500
Module gno.land/r/gnoswap/gov/governance/v1
gno 0.9 proposal_data.gno gno
⧉
1 package governance 2 3 import ( 4 "strings" 5 6 "gno.land/p/gnoswap/utils/v1" 7 ufmt "gno.land/p/nt/ufmt/v0" 8 Gnoland RPC unavailable (functions): rpc abci_query: HTTP 500
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
Render unavailable
rate limit exceeded — the render query is served by the Gnoland archive RPC.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
9 "gno.land/r/gnoswap/gov/governance"
10 )
11
12 type ProposalMetadataResolver struct {
13 *governance.ProposalMetadata
14 }
15
16 // NewProposalMetadataResolver wraps proposal metadata for validation.
17 //
18 // Parameters:
19 // - metadata: proposal title and description to validate.
20 //
21 // Returns:
22 // - *ProposalMetadataResolver: resolver backed by metadata.
23 func NewProposalMetadataResolver(metadata *governance.ProposalMetadata) *ProposalMetadataResolver {
24 return &ProposalMetadataResolver{
25 ProposalMetadata: metadata,
26 }
27 }
28
29 // Validate performs comprehensive validation of the proposal metadata.
30 // Checks title and description length and content requirements.
31 //
32 // Returns:
33 // - error: validation error if metadata is invalid
34 func (r *ProposalMetadataResolver) Validate() error {
35 // Validate title meets requirements
36 if err := r.validateTitle(r.Title()); err != nil {
37 return err
38 }
39
40 // Validate description meets requirements
41 if err := r.validateDescription(r.Description()); err != nil {
42 return err
43 }
44
45 return nil
46 }
47
48 // validateTitle checks if the proposal title meets length and content requirements.
49 //
50 // Parameters:
51 // - title: title string to validate
52 //
53 // Returns:
54 // - error: validation error if title is invalid
55 func (r *ProposalMetadataResolver) validateTitle(title string ) error {
56 // Title cannot be empty
57 if title == "" {
58 return makeErrorWithDetails(errInvalidInput, "title is empty" )
59 }
60
61 // Title cannot exceed maximum length
62 if len(title) > maxTitleLength {
63 return makeErrorWithDetails(
64 errInvalidInput,
65 "title is too long, max length is 255 characters" ,
66 )
67 }
68
69 return nil
70 }
71
72 // validateDescription checks if the proposal description meets length and content requirements.
73 //
74 // Parameters:
75 // - description: description string to validate
76 //
77 // Returns:
78 // - error: validation error if description is invalid
79 func (r *ProposalMetadataResolver) validateDescription(description string ) error {
80 // Description cannot be empty
81 if description == "" {
82 return makeErrorWithDetails(
83 errInvalidInput,
84 "description is empty" ,
85 )
86 }
87
88 // Description cannot exceed maximum length
89 if len(description) > maxDescriptionLength {
90 return makeErrorWithDetails(
91 errInvalidInput,
92 "description is too long, max length is 10,000 characters" ,
93 )
94 }
95
96 return nil
97 }
98
99 // ProposalDataResolver handles business logic for proposal data.
100 type ProposalDataResolver struct {
101 *governance.ProposalData
102 }
103
104 // NewProposalDataResolver wraps proposal data for type-specific validation and
105 // execution-message parsing.
106 //
107 // Parameters:
108 // - proposalData: proposal data to resolve and validate.
109 //
110 // Returns:
111 // - *ProposalDataResolver: resolver backed by proposalData.
112 func NewProposalDataResolver(proposalData *governance.ProposalData) *ProposalDataResolver {
113 return &ProposalDataResolver{
114 ProposalData: proposalData,
115 }
116 }
117
118 // Validate performs type-specific validation of the proposal data.
119 // Different proposal types have different validation requirements.
120 //
121 // Returns:
122 // - error: validation error if data is invalid
123 func (r *ProposalDataResolver) Validate() error {
124 switch r.ProposalType() {
125 case governance.Text:
126 return r.validateText()
127 case governance.CommunityPoolSpend:
128 return r.validateCommunityPoolSpend()
129 case governance.ParameterChange:
130 return r.validateParameterChange()
131 }
132 return nil
133 }
134
135 // validateText validates text proposal data.
136 // Text proposals have no additional validation requirements.
137 //
138 // Returns:
139 // - error: always nil for text proposals
140 func (r *ProposalDataResolver) validateText() error {
141 return nil
142 }
143
144 // validateCommunityPoolSpend validates community pool spend proposal data.
145 // Checks recipient address, token path, and amount validity.
146 //
147 // Returns:
148 // - error: validation error if community pool spend data is invalid
149 func (r *ProposalDataResolver) validateCommunityPoolSpend() error {
150 // Validate recipient address
151 communityPoolSpend := r.ProposalData.CommunityPoolSpend()
152 if communityPoolSpend == nil {
153 return makeErrorWithDetails(
154 errInvalidInput, "community pool spend info is missing" )
155 }
156
157 if !communityPoolSpend.To().IsValid() {
158 return makeErrorWithDetails(
159 errInvalidInput, "to is invalid address" )
160 }
161
162 // Validate amount is greater than 0
163 if communityPoolSpend.Amount() <= 0 {
164 return makeErrorWithDetails(
165 errInvalidInput, "amount is not positive" )
166 }
167
168 return nil
169 }
170
171 // validateParameterChange validates parameter change proposal data.
172 // Delegates to validateExecutions for full validation including count checks,
173 // message format, handler existence, and parameter type validation.
174 //
175 // Returns:
176 // - error: validation error if parameter change data is invalid
177 func (r *ProposalDataResolver) validateParameterChange() error {
178 execution := r.Execution()
179 if execution == nil {
180 return makeErrorWithDetails(
181 errInvalidInput,
182 "execution info is missing" ,
183 )
184 }
185 return validateExecutions(execution.Num(), execution.Msgs())
186 }
187
188 // ParameterChangesInfos parses the execution messages and returns structured parameter change information.
189 // Each message is expected to be in format: pkgPath*EXE*function*EXE*params
190 //
191 // Returns:
192 // - []ParameterChangeInfo: slice of parsed parameter change information
193 // - error: validation error if any execution message is malformed
194 func (r *ProposalDataResolver) ParameterChangesInfos() ([]governance.ParameterChangeInfo, error ) {
195 infos := make([]governance.ParameterChangeInfo, 0 )
196
197 // Return empty slice if no executions
198 execution := r.Execution()
199 if execution == nil || execution.Num() <= 0 {
200 return infos, nil
201 }
202
203 // Parse each execution message
204 for _, msg := range execution.Msgs() {
205 pkgPath, function, params, partCount := parseExecutionMessage(msg)
206 if partCount != 3 {
207 return nil , makeErrorWithDetails(
208 errInvalidMessageFormat,
209 ufmt.Sprintf( "malformed execution message: expected 3 parts (pkgPath, function, params), got %d" , partCount),
210 )
211 }
212
213 // Create parameter change info structure
214 info := governance.NewParameterChangeInfo(pkgPath, function, params)
215 infos = append(infos, info)
216 }
217
218 return infos, nil
219 }
220
221 // NewProposalTextData creates proposal data for a text proposal.
222 // Text proposals have no additional data requirements.
223 //
224 // Returns:
225 // - *ProposalData: proposal data configured for text proposal
226 func NewProposalTextData() *governance.ProposalData {
227 return governance.NewProposalData(governance.Text, nil , nil )
228 }
229
230 // NewProposalCommunityPoolSpendData creates proposal data for a community pool spend proposal.
231 // Automatically generates the execution message for the token transfer.
232 //
233 // Parameters:
234 // - tokenPath: path of the token to transfer
235 // - to: recipient address for the transfer
236 // - amount: amount of tokens to transfer
237 // - communityPoolPackagePath: package path of the community pool contract
238 //
239 // Returns:
240 // - *ProposalData: proposal data configured for community pool spending
241 func NewProposalCommunityPoolSpendData(
242 tokenPath string ,
243 to address,
244 amount int64 ,
245 communityPoolPackagePath string ,
246 ) *governance.ProposalData {
247 // Create execution message for the token transfer
248 executionInfoMessage := makeExecuteMessage(
249 communityPoolPackagePath,
250 "TransferToken" ,
251 [] string {tokenPath, to.String(), utils.FormatInt(amount)},
252 )
253
254 return governance.NewProposalData(
255 governance.CommunityPoolSpend,
256 governance.NewCommunityPoolSpendInfo(to, tokenPath, amount),
257 governance.NewExecutionInfo( 1 , [] string {executionInfoMessage}),
258 )
259 }
260
261 // NewProposalExecutionData creates proposal data for a parameter change proposal.
262 // Each message in executions should be formatted as <pkgPath>*EXE*<function>*EXE*<params>,
263 // separated by *GOV* when there are multiple messages.
264 //
265 // Parameters:
266 // - numToExecute: number of parameter changes to execute
267 // - executions: raw encoded execution string with parameter changes
268 //
269 // Returns:
270 // - *ProposalData: proposal data configured for parameter changes
271 func NewProposalExecutionData(numToExecute int64 , executions string ) *governance.ProposalData {
272 return governance.NewProposalData(
273 governance.ParameterChange,
274 nil ,
275 governance.NewExecutionInfo(numToExecute, splitExecutionsRaw(executions)),
276 )
277 }
278
279 // makeExecuteMessage creates a message to execute a function.
280 // Message format: <pkgPath>*EXE*<function>*EXE*<params>.
281 func makeExecuteMessage(pkgPath, function string , params [] string ) string {
282 messageParams := [] string {
283 pkgPath,
284 function,
285 strings.Join(params, "," ),
286 }
287 return strings.Join(messageParams, parameterSeparator)
288 }
289