parameter_registry.gno
36.99 Kb · 1270 lines
1package governance
2
3import (
4 "strings"
5
6 prbac "gno.land/p/gnoswap/rbac/v1"
7 ufmt "gno.land/p/nt/ufmt/v0"
8
9 cm "gno.land/r/gnoswap/common"
10 cp "gno.land/r/gnoswap/community_pool/v1"
11 en "gno.land/r/gnoswap/emission"
12 "gno.land/r/gnoswap/rbac/v1"
13
14 "gno.land/r/gnoswap/gov/governance"
15 gs "gno.land/r/gnoswap/gov/staker"
16 lp "gno.land/r/gnoswap/launchpad"
17 pl "gno.land/r/gnoswap/pool"
18 pos "gno.land/r/gnoswap/position"
19 pf "gno.land/r/gnoswap/protocol_fee"
20 rr "gno.land/r/gnoswap/router"
21 sr "gno.land/r/gnoswap/staker"
22
23 "gno.land/r/gnoswap/halt/v1"
24)
25
26// globalParameterRegistry is initialized once and reused for all validation and execution.
27// This provides consistent validation at proposal creation time and execution time.
28var globalParameterRegistry *ParameterRegistry
29
30func init() {
31 globalParameterRegistry = CreateParameterHandlers()
32}
33
34// Package paths
35const (
36 GNS_TOKEN_KEY = "gno.land/r/gnoswap/gns.GNS"
37 HALT_PATH = "gno.land/r/gnoswap/halt/v1"
38 RBAC_PATH = "gno.land/r/gnoswap/rbac/v1"
39 ACCESS_PATH = "gno.land/r/gnoswap/access/v1"
40 EMISSION_PATH = "gno.land/r/gnoswap/emission"
41 COMMON_PATH = "gno.land/r/gnoswap/common"
42 POOL_PATH = "gno.land/r/gnoswap/pool"
43 POSITION_PATH = "gno.land/r/gnoswap/position"
44 ROUTER_PATH = "gno.land/r/gnoswap/router"
45 STAKER_PATH = "gno.land/r/gnoswap/staker"
46 LAUNCHPAD_PATH = "gno.land/r/gnoswap/launchpad"
47 PROTOCOL_FEE_PATH = "gno.land/r/gnoswap/protocol_fee"
48 COMMUNITY_POOL_PATH = "gno.land/r/gnoswap/community_pool/v1"
49 GOV_GOVERNANCE_PATH = "gno.land/r/gnoswap/gov/governance"
50 GOV_STAKER_PATH = "gno.land/r/gnoswap/gov/staker"
51)
52
53// ParameterHandler interface defines the contract for parameter execution handlers.
54// Each handler is responsible for executing specific parameter changes in the system.
55type ParameterHandler interface {
56 // Execute processes the parameters and applies the changes to the system.
57 // The `_ int, rlm realm` discriminator pair forwards the governance proxy's
58 // realm value into the handler so any cross-realm calls inside the closure
59 // run under the proxy's identity (the only address with caller-allowlist
60 // permission against the targeted /r/ realms).
61 //
62 // Parameters:
63 // - _: integer discriminator required by the crossing entrypoint; callers use 0
64 // - rlm: current governance proxy realm context forwarded to the handler
65 // - params: serialized parameter values to validate and apply in handler order
66 //
67 // Returns:
68 // - error: nil when the parameter change succeeds, or an execution/validation error
69 Execute(_ int, rlm realm, params []string) error
70}
71
72// ParameterHandlerOptions contains the configuration and execution logic for a parameter handler.
73// This struct encapsulates all information needed to identify and execute a parameter change.
74//
75// NOTE: handlerFunc uses `rlm realm` (rather than `cur realm`) as the realm
76// parameter name. The v2 preprocessor reserves the `cur` name for the first
77// realm-type parameter of top-level crossing function declarations and
78// `t.Run` closures only; using it as the realm-parameter name on a
79// multi-parameter struct-field function value trips a parser check
80// ("only the first realm type argument of a crossing function may have name
81// `cur`"). Naming it `rlm` keeps the lowering identical without the syntax
82// constraint.
83type ParameterHandlerOptions struct {
84 pkgPath string // Package path of the target contract
85 function string // Function name to be called
86 paramCount int // Expected number of parameters
87 handlerFunc func(_ int, rlm realm, _ []string) error // Function that executes the parameter change
88 paramValidators []paramValidator // Optional per-parameter validators for proposal-time checks
89 compositeValidator compositeValidator // Optional cross-parameter validator for static business rules
90}
91
92// paramValidator validates a single parameter value and returns an error on failure.
93type paramValidator func(string) error
94
95// compositeValidator validates relationships between a handler's parameters.
96// It must only enforce deterministic, state-independent rules so that a
97// proposal is not rejected merely because on-chain state changes while voting.
98type compositeValidator func([]string) error
99
100// NewParameterHandlerOptions creates a new parameter handler with the specified configuration.
101//
102// Parameters:
103// - pkgPath: package path of the target contract
104// - function: function name to be called
105// - paramCount: expected number of parameters
106// - handlerFunc: callback receiving the discriminator, propagated realm, and serialized parameters to execute the change
107// - paramValidators: optional validators for each parameter (must match paramCount if provided)
108//
109// Returns:
110// - ParameterHandler: configured parameter handler interface
111func NewParameterHandlerOptions(
112 pkgPath,
113 function string,
114 paramCount int,
115 handlerFunc func(_ int, rlm realm, _ []string) error,
116 paramValidators ...paramValidator,
117) ParameterHandler {
118 if len(paramValidators) > 0 && len(paramValidators) != paramCount {
119 panic(ufmt.Sprintf(
120 "invalid validator count for %s:%s: expected %d, got %d",
121 pkgPath, function, paramCount, len(paramValidators),
122 ))
123 }
124
125 return &ParameterHandlerOptions{
126 pkgPath: pkgPath,
127 function: function,
128 paramCount: paramCount,
129 handlerFunc: handlerFunc,
130 paramValidators: paramValidators,
131 }
132}
133
134// HandlerKey generates a unique key for this handler based on package path and function name.
135//
136// Returns:
137// - string: unique identifier for the handler
138func (h *ParameterHandlerOptions) HandlerKey() string {
139 return makeHandlerKey(h.pkgPath, h.function)
140}
141
142// Execute validates parameter count and executes the handler function.
143// This method ensures the correct number of parameters are provided before execution.
144//
145// Parameters:
146// - _: integer discriminator required by the crossing entrypoint; callers use 0
147// - rlm: governance proxy realm threaded into the wrapped handler
148// - params: serialized parameter values to pass to the handler
149//
150// Returns:
151// - error: parameter-count error or the error returned by the wrapped handler
152func (h *ParameterHandlerOptions) Execute(_ int, rlm realm, params []string) error {
153 if len(params) != h.paramCount {
154 return ufmt.Errorf("expected %d parameters, got %d", h.paramCount, len(params))
155 }
156
157 return h.handlerFunc(0, rlm, params)
158}
159
160// ValidateParams checks the parameter count and runs configured individual and composite validators without executing the handler.
161//
162// Parameters:
163// - params: serialized parameter values to validate in handler order
164//
165// Returns:
166// - error: nil when all configured checks pass, or the first count/type/business-rule validation error
167func (h *ParameterHandlerOptions) ValidateParams(params []string) error {
168 if len(params) != h.paramCount {
169 return ufmt.Errorf(
170 "expected %d parameters for %s, got %d",
171 h.paramCount, h.HandlerKey(), len(params),
172 )
173 }
174
175 if len(h.paramValidators) > 0 {
176 if len(h.paramValidators) != h.paramCount {
177 return ufmt.Errorf(
178 "validator count mismatch for %s: expected %d validator(s), got %d",
179 h.HandlerKey(), h.paramCount, len(h.paramValidators),
180 )
181 }
182
183 for i, validator := range h.paramValidators {
184 if validator == nil {
185 continue
186 }
187
188 if err := validator(params[i]); err != nil {
189 return ufmt.Errorf("param[%d]: %v", i, err)
190 }
191 }
192 }
193
194 if h.compositeValidator != nil {
195 if err := h.compositeValidator(params); err != nil {
196 return err
197 }
198 }
199
200 return nil
201}
202
203// ParameterRegistry manages the collection of parameter handlers for governance execution.
204// This registry allows proposals to execute parameter changes across different system contracts.
205type ParameterRegistry struct {
206 handlers map[string]ParameterHandlerOptions // Map storing handler configurations keyed by package:function
207}
208
209// Register adds a new parameter handler to the registry.
210// Each handler is identified by a unique combination of package path and function name.
211//
212// Parameters:
213// - handler: parameter handler configuration to register
214func (r *ParameterRegistry) Register(handler ParameterHandlerOptions) {
215 r.handlers[handler.HandlerKey()] = handler
216}
217
218// Handler retrieves a parameter handler by its package:function key.
219// This method is used during proposal execution to find the appropriate handler.
220//
221// Parameters:
222// - key: handler key returned by HandlerKey, in "pkgPath:function" format
223//
224// Returns:
225// - ParameterHandler: the matching parameter handler
226// - error: error if the key has no registered handler
227func (r *ParameterRegistry) Handler(key string) (ParameterHandler, error) {
228 // Retrieve handler from registry
229 handler, exists := r.handlers[key]
230 if !exists {
231 return nil, ufmt.Errorf("handler not found for %s", key)
232 }
233
234 return &handler, nil
235}
236
237// NewParameterRegistry creates a new empty parameter registry.
238//
239// Returns:
240// - *ParameterRegistry: new registry instance
241func NewParameterRegistry() *ParameterRegistry {
242 return &ParameterRegistry{handlers: make(map[string]ParameterHandlerOptions)}
243}
244
245// makeHandlerKey creates a unique identifier for a handler based on package path and function.
246//
247// Parameters:
248// - pkgPath: package path of the target contract
249// - function: function name to be called
250//
251// Returns:
252// - string: unique key in format "pkgPath:function"
253func makeHandlerKey(pkgPath, function string) string {
254 return pkgPath + ":" + function
255}
256
257// CreateParameterHandlers initializes and configures all supported parameter handlers.
258// This function defines all the parameter changes that can be executed through governance proposals.
259// It covers configuration changes for various system components including pools, staking, fees, etc.
260//
261// Returns:
262// - *ParameterRegistry: fully configured registry with all supported handlers
263func CreateParameterHandlers() *ParameterRegistry {
264 registry := NewParameterRegistry()
265
266 // Define all handler configurations for different system components.
267 //
268 // Each closure receives `(_ int, rlm realm, params []string)` and threads
269 // `rlm` into the targeted /r/ realm call. The closure is invoked from
270 // ParameterHandlerOptions.Execute which forwards the governance proxy's
271 // realm value, so cross-realm calls land on the targeted realm with the
272 // governance proxy as the immediate caller.
273 handlers := []*ParameterHandlerOptions{
274 // Community pool token transfers
275 {
276 pkgPath: COMMUNITY_POOL_PATH,
277 function: "TransferToken",
278 paramCount: 3,
279 paramValidators: []paramValidator{
280 stringValidator, // pkgPath
281 addressValidator, // to
282 positiveInt64Validator("amount"),
283 },
284 handlerFunc: func(_ int, rlm realm, params []string) error {
285 // Transfer tokens from community pool to specified address
286 cp.TransferToken(
287 cross(rlm),
288 params[0], // pkgPath
289 address(params[1]), // to
290 parseNumber(params[2], kindInt64).(int64), // amount
291 )
292
293 return nil
294 },
295 },
296 // Emission distribution configuration
297 {
298 pkgPath: EMISSION_PATH,
299 function: "SetDistributionStartTime",
300 paramCount: 1,
301 paramValidators: []paramValidator{
302 numberValidator(kindInt64), // start time
303 },
304 handlerFunc: func(_ int, rlm realm, params []string) error {
305 // Set distribution start time
306 en.SetDistributionStartTime(cross(rlm), parseInt64(params[0]))
307
308 return nil
309 },
310 },
311 {
312 pkgPath: EMISSION_PATH,
313 function: "ChangeDistributionPct",
314 paramCount: 4,
315 paramValidators: []paramValidator{
316 numberValidator(kindInt64), // liquidityStakerPct
317 numberValidator(kindInt64), // devOpsPct
318 numberValidator(kindInt64), // communityPoolPct
319 numberValidator(kindInt64), // govStakerPct
320 },
321 compositeValidator: distributionPctCompositeValidator,
322 handlerFunc: func(_ int, rlm realm, params []string) error {
323 liquidityStakerPct := parseNumber(params[0], kindInt64).(int64)
324 devOpsPct := parseNumber(params[1], kindInt64).(int64)
325 communityPoolPct := parseNumber(params[2], kindInt64).(int64)
326 govStakerPct := parseNumber(params[3], kindInt64).(int64)
327
328 en.ChangeDistributionPct(
329 cross(rlm),
330 liquidityStakerPct,
331 devOpsPct,
332 communityPoolPct,
333 govStakerPct,
334 )
335
336 return nil
337 },
338 },
339 // Governance configuration changes
340 {
341 pkgPath: GOV_GOVERNANCE_PATH,
342 function: "Reconfigure",
343 paramCount: 7,
344 paramValidators: []paramValidator{
345 numberValidator(kindInt64), // votingStartDelay
346 numberValidator(kindInt64), // votingPeriod
347 numberValidator(kindInt64), // votingWeightSmoothingDuration
348 numberValidator(kindInt64), // quorum
349 numberValidator(kindInt64), // proposalCreationThreshold
350 numberValidator(kindInt64), // executionDelay
351 numberValidator(kindInt64), // executionWindow
352 },
353 handlerFunc: func(_ int, rlm realm, params []string) error {
354 // Parse governance configuration parameters
355 votingStartDelay := parseInt64(params[0])
356 votingPeriod := parseInt64(params[1])
357 votingWeightSmoothingDuration := parseInt64(params[2])
358 quorum := parseInt64(params[3])
359 proposalCreationThreshold := parseInt64(params[4])
360 executionDelay := parseInt64(params[5])
361 executionWindow := parseInt64(params[6])
362
363 // Reconfigure governance parameters through governance process
364 governance.Reconfigure(
365 cross(rlm),
366 votingStartDelay,
367 votingPeriod,
368 votingWeightSmoothingDuration,
369 quorum,
370 proposalCreationThreshold,
371 executionDelay,
372 executionWindow,
373 )
374
375 return nil
376 },
377 },
378
379 // Pool protocol fee configuration
380 {
381 pkgPath: POOL_PATH,
382 function: "CollectProtocol",
383 paramCount: 6,
384 paramValidators: []paramValidator{
385 stringValidator, // token0Path
386 stringValidator, // token1Path
387 uint64Validator, // fee
388 addressValidator, // recipient
389 nonNegativeInt64Validator("amount0Requested"),
390 nonNegativeInt64Validator("amount1Requested"),
391 },
392 handlerFunc: func(_ int, rlm realm, params []string) error {
393 pl.CollectProtocol(
394 cross(rlm),
395 params[0], // token0Path
396 params[1], // token1Path
397 uint32(parseUint64(params[2])), // fee
398 address(params[3]), // recipient
399 params[4], // amount0Requested
400 params[5], // amount1Requested
401 )
402
403 return nil
404 },
405 },
406 {
407 pkgPath: POOL_PATH,
408 function: "SetFeeProtocol",
409 paramCount: 2,
410 paramValidators: []paramValidator{
411 uint8RangeValidator("feeProtocol0"),
412 uint8RangeValidator("feeProtocol1"),
413 },
414 handlerFunc: func(_ int, rlm realm, params []string) error {
415 // Parse and validate fee protocol values
416 feeProtocol0 := parseInt64(params[0])
417 feeProtocol1 := parseInt64(params[1])
418
419 // Validate fee protocol values are within uint8 range
420 if feeProtocol0 > 255 {
421 panic(ufmt.Sprintf("feeProtocol0 out of range: %d", feeProtocol0))
422 }
423
424 if feeProtocol1 > 255 {
425 panic(ufmt.Sprintf("feeProtocol1 out of range: %d", feeProtocol1))
426 }
427
428 // Set protocol fee percentages
429 pl.SetFeeProtocol(
430 cross(rlm),
431 uint8(feeProtocol0), // feeProtocol0
432 uint8(feeProtocol1), // feeProtocol1
433 )
434
435 return nil
436 },
437 },
438 // Pool creation fee
439 {
440 pkgPath: POOL_PATH,
441 function: "SetPoolCreationFee",
442 paramCount: 1,
443 paramValidators: []paramValidator{
444 nonNegativeInt64Validator("fee"),
445 },
446 handlerFunc: func(_ int, rlm realm, params []string) error {
447 // Set fee required to create new pools
448 pl.SetPoolCreationFee(cross(rlm), parseInt64(params[0])) // fee
449 return nil
450 },
451 },
452 // Pool withdrawal fee
453 {
454 pkgPath: POOL_PATH,
455 function: "SetWithdrawalFee",
456 paramCount: 1,
457 paramValidators: []paramValidator{
458 uint64RangeValidator("fee", 1000),
459 },
460 handlerFunc: func(_ int, rlm realm, params []string) error {
461 // Set fee for withdrawing from pools
462 pl.SetWithdrawalFee(cross(rlm), parseUint64(params[0])) // fee
463 return nil
464 },
465 },
466
467 // Protocol fee distribution
468 {
469 pkgPath: PROTOCOL_FEE_PATH,
470 function: "SetDevOpsPct",
471 paramCount: 1,
472 paramValidators: []paramValidator{
473 int64RangeValidator("pct", 0, 10000),
474 },
475 handlerFunc: func(_ int, rlm realm, params []string) error {
476 // Set percentage of protocol fees going to development operations
477 pf.SetDevOpsPct(cross(rlm), parseInt64(params[0])) // pct
478 return nil
479 },
480 },
481
482 // Router swap fee
483 {
484 pkgPath: ROUTER_PATH,
485 function: "SetSwapFee",
486 paramCount: 1,
487 paramValidators: []paramValidator{
488 uint64RangeValidator("fee", 1000),
489 },
490 handlerFunc: func(_ int, rlm realm, params []string) error {
491 // Set fee charged for token swaps
492 rr.SetSwapFee(cross(rlm), parseUint64(params[0])) // fee
493 return nil
494 },
495 },
496
497 // Staker configuration handlers
498 {
499 pkgPath: STAKER_PATH,
500 function: "SetDepositGnsAmount",
501 paramCount: 1,
502 paramValidators: []paramValidator{
503 nonNegativeInt64Validator("amount"),
504 },
505 handlerFunc: func(_ int, rlm realm, params []string) error {
506 // Set minimum GNS amount required for staking deposits
507 sr.SetDepositGnsAmount(cross(rlm), parseInt64(params[0])) // amount
508 return nil
509 },
510 },
511 {
512 pkgPath: STAKER_PATH,
513 function: "SetMinimumRewardAmount",
514 paramCount: 1,
515 paramValidators: []paramValidator{
516 nonNegativeInt64Validator("amount"),
517 },
518 handlerFunc: func(_ int, rlm realm, params []string) error {
519 // Set minimum GNS amount required for staking deposits
520 sr.SetMinimumRewardAmount(cross(rlm), parseInt64(params[0])) // amount
521 return nil
522 },
523 },
524 {
525 pkgPath: STAKER_PATH,
526 function: "SetTokenMinimumRewardAmount",
527 paramCount: 1,
528 paramValidators: []paramValidator{
529 tokenMinimumRewardAmountValidator,
530 },
531 handlerFunc: func(_ int, rlm realm, params []string) error {
532 // Set minimum GNS amount required for staking deposits
533 // params[0] is a string in the format "tokenPath:amount"
534 sr.SetTokenMinimumRewardAmount(cross(rlm), params[0]) // amount
535 return nil
536 },
537 },
538 {
539 pkgPath: STAKER_PATH,
540 function: "SetPoolTier",
541 paramCount: 2,
542 paramValidators: []paramValidator{
543 stringValidator, // pool
544 uint64RangeValidator("tier", sr.AllTierCount-1),
545 },
546 handlerFunc: func(_ int, rlm realm, params []string) error {
547 // Assign tier level to a specific pool
548 sr.SetPoolTier(
549 cross(rlm),
550 params[0], // pool
551 parseUint64(params[1]), // tier
552 )
553 return nil
554 },
555 },
556 {
557 pkgPath: STAKER_PATH,
558 function: "ChangePoolTier",
559 paramCount: 2,
560 paramValidators: []paramValidator{
561 stringValidator, // pool
562 uint64RangeValidator("tier", sr.AllTierCount-1),
563 },
564 handlerFunc: func(_ int, rlm realm, params []string) error {
565 // Change existing pool's tier level
566 sr.ChangePoolTier(
567 cross(rlm),
568 params[0], // pool
569 parseUint64(params[1]), // tier
570 )
571 return nil
572 },
573 },
574 {
575 pkgPath: STAKER_PATH,
576 function: "RemovePoolTier",
577 paramCount: 1,
578 paramValidators: []paramValidator{
579 stringValidator, // pool
580 },
581 handlerFunc: func(_ int, rlm realm, params []string) error {
582 // Remove tier assignment from a pool
583 sr.RemovePoolTier(cross(rlm), params[0]) // pool
584 return nil
585 },
586 },
587 {
588 pkgPath: STAKER_PATH,
589 function: "SetUnStakingFee",
590 paramCount: 1,
591 paramValidators: []paramValidator{
592 uint64RangeValidator("fee", 1000),
593 },
594 handlerFunc: func(_ int, rlm realm, params []string) error {
595 // Set fee charged for unstaking operations
596 fee := parseUint64(params[0])
597 sr.SetUnStakingFee(cross(rlm), fee)
598 return nil
599 },
600 },
601 {
602 pkgPath: STAKER_PATH,
603 function: "SetWarmUp",
604 paramCount: 2,
605 paramValidators: []paramValidator{
606 numberValidator(kindInt64), // percent
607 numberValidator(kindInt64), // block
608 },
609 handlerFunc: func(_ int, rlm realm, params []string) error {
610 // Set warm-up period configuration for staking
611 percent := parseInt64(params[0])
612 block := parseNumber(params[1], kindInt64).(int64)
613 sr.SetWarmUp(cross(rlm), percent, block)
614
615 return nil
616 },
617 },
618
619 // System halt controls
620 {
621 pkgPath: HALT_PATH,
622 function: "SetHaltLevel",
623 paramCount: 1,
624 paramValidators: []paramValidator{
625 haltLevelValidator,
626 },
627 handlerFunc: func(_ int, rlm realm, params []string) error {
628 // Set system-wide halt status
629 halt.SetHaltLevel(cross(rlm), halt.HaltLevel(params[0])) // true = halt, false = no halt
630
631 return nil
632 },
633 },
634 {
635 pkgPath: HALT_PATH,
636 function: "SetOperationStatus",
637 paramCount: 2,
638 paramValidators: []paramValidator{
639 haltOperationTypeValidator,
640 boolValidator, // allowed
641 },
642 handlerFunc: func(_ int, rlm realm, params []string) error {
643 // Enable or disable specific operation types
644 opType := halt.OpType(params[0])
645 allowed := parseBool(params[1])
646
647 halt.SetOperationStatus(cross(rlm), opType, allowed)
648
649 return nil
650 },
651 },
652
653 // RBAC configuration
654 {
655 pkgPath: RBAC_PATH,
656 function: "RegisterRole",
657 paramCount: 2,
658 paramValidators: []paramValidator{
659 roleNameValidator,
660 addressValidator, // roleAddress
661 },
662 handlerFunc: func(_ int, rlm realm, params []string) error {
663 roleName := params[0]
664 roleAddress := address(params[1])
665
666 // Register a new role
667 rbac.RegisterRole(cross(rlm), roleName, roleAddress)
668
669 return nil
670 },
671 },
672 {
673 pkgPath: RBAC_PATH,
674 function: "UpdateRoleAddress",
675 paramCount: 2,
676 paramValidators: []paramValidator{
677 updatableRoleNameValidator,
678 addressValidator, // roleAddress
679 },
680 handlerFunc: func(_ int, rlm realm, params []string) error {
681 roleName := params[0]
682 roleAddress := address(params[1])
683
684 // Update role address
685 rbac.UpdateRoleAddress(cross(rlm), roleName, roleAddress)
686
687 return nil
688 },
689 },
690 {
691 pkgPath: RBAC_PATH,
692 function: "RemoveRole",
693 paramCount: 1,
694 paramValidators: []paramValidator{
695 removableRoleNameValidator,
696 },
697 handlerFunc: func(_ int, rlm realm, params []string) error {
698 roleName := params[0]
699
700 // Remove role
701 rbac.RemoveRole(cross(rlm), roleName)
702
703 return nil
704 },
705 },
706
707 // Protocol fee - SetGovStakerPct
708 {
709 pkgPath: PROTOCOL_FEE_PATH,
710 function: "SetGovStakerPct",
711 paramCount: 1,
712 paramValidators: []paramValidator{
713 int64RangeValidator("pct", 0, 10000),
714 },
715 handlerFunc: func(_ int, rlm realm, params []string) error {
716 // Set percentage of protocol fees going to governance stakers
717 pf.SetGovStakerPct(cross(rlm), parseInt64(params[0])) // pct
718 return nil
719 },
720 },
721
722 // Staker - Token list management
723 {
724 pkgPath: STAKER_PATH,
725 function: "AddToken",
726 paramCount: 1,
727 paramValidators: []paramValidator{
728 stringValidator, // tokenPath
729 },
730 handlerFunc: func(_ int, rlm realm, params []string) error {
731 // Add token to allowed token list for external incentives
732 sr.AddToken(cross(rlm), params[0]) // tokenPath
733 return nil
734 },
735 },
736 {
737 pkgPath: STAKER_PATH,
738 function: "RemoveToken",
739 paramCount: 1,
740 paramValidators: []paramValidator{
741 stringValidator, // tokenPath
742 },
743 handlerFunc: func(_ int, rlm realm, params []string) error {
744 // Remove token from allowed token list
745 sr.RemoveToken(cross(rlm), params[0]) // tokenPath
746 return nil
747 },
748 },
749 {
750 pkgPath: STAKER_PATH,
751 function: "SetDeniedRewardToken",
752 paramCount: 2,
753 paramValidators: []paramValidator{
754 stringValidator, // tokenPath
755 boolValidator, // denied
756 },
757 handlerFunc: func(_ int, rlm realm, params []string) error {
758 // Deny or re-allow a token as an external incentive reward token
759 sr.SetDeniedRewardToken(cross(rlm), params[0], parseBool(params[1]))
760 return nil
761 },
762 },
763
764 // Staker - External incentive recovery
765 {
766 pkgPath: STAKER_PATH,
767 function: "CancelExternalIncentive",
768 paramCount: 2,
769 paramValidators: []paramValidator{
770 stringValidator, // targetPoolPath
771 stringValidator, // incentiveId
772 },
773 handlerFunc: func(_ int, rlm realm, params []string) error {
774 // Cancel a not-yet-started external incentive; the reward tokens
775 // and the GNS deposit are refunded to the incentive creator
776 sr.CancelExternalIncentive(
777 cross(rlm),
778 params[0], // targetPoolPath
779 params[1], // incentiveId
780 )
781 return nil
782 },
783 },
784
785 // Launchpad - Project management
786 {
787 pkgPath: LAUNCHPAD_PATH,
788 function: "CreateProject",
789 paramCount: 10,
790 paramValidators: []paramValidator{
791 stringValidator, // name
792 stringValidator, // tokenPath
793 addressValidator, // recipient
794 numberValidator(kindInt64), // depositAmount
795 stringValidator, // conditionTokens
796 stringValidator, // conditionAmounts
797 numberValidator(kindInt64), // tier30Ratio
798 numberValidator(kindInt64), // tier90Ratio
799 numberValidator(kindInt64), // tier180Ratio
800 numberValidator(kindInt64), // startTime
801 },
802 handlerFunc: func(_ int, rlm realm, params []string) error {
803 // Create a new launchpad project
804 lp.CreateProject(
805 cross(rlm),
806 params[0], // name
807 params[1], // tokenPath
808 address(params[2]), // recipient
809 parseNumber(params[3], kindInt64).(int64), // depositAmount
810 params[4], // conditionTokens
811 params[5], // conditionAmounts
812 parseNumber(params[6], kindInt64).(int64), // tier30Ratio
813 parseNumber(params[7], kindInt64).(int64), // tier90Ratio
814 parseNumber(params[8], kindInt64).(int64), // tier180Ratio
815 parseNumber(params[9], kindInt64).(int64), // startTime
816 )
817 return nil
818 },
819 },
820 // Upgrade handlers for various domains
821 {
822 pkgPath: POOL_PATH,
823 function: "UpgradeImpl",
824 paramCount: 1,
825 paramValidators: []paramValidator{
826 stringValidator, // packagePath
827 },
828 handlerFunc: func(_ int, rlm realm, params []string) error {
829 // Upgrade pool implementation
830 pl.UpgradeImpl(cross(rlm), params[0]) // packagePath
831 return nil
832 },
833 },
834 {
835 pkgPath: POSITION_PATH,
836 function: "UpgradeImpl",
837 paramCount: 1,
838 paramValidators: []paramValidator{
839 stringValidator, // packagePath
840 },
841 handlerFunc: func(_ int, rlm realm, params []string) error {
842 // Upgrade position implementation
843 pos.UpgradeImpl(cross(rlm), params[0]) // packagePath
844 return nil
845 },
846 },
847 {
848 pkgPath: STAKER_PATH,
849 function: "UpgradeImpl",
850 paramCount: 1,
851 paramValidators: []paramValidator{
852 stringValidator, // packagePath
853 },
854 handlerFunc: func(_ int, rlm realm, params []string) error {
855 // Upgrade staker implementation
856 sr.UpgradeImpl(cross(rlm), params[0]) // packagePath
857 return nil
858 },
859 },
860 {
861 pkgPath: LAUNCHPAD_PATH,
862 function: "UpgradeImpl",
863 paramCount: 1,
864 paramValidators: []paramValidator{
865 stringValidator, // packagePath
866 },
867 handlerFunc: func(_ int, rlm realm, params []string) error {
868 // Upgrade launchpad implementation
869 lp.UpgradeImpl(cross(rlm), params[0]) // packagePath
870 return nil
871 },
872 },
873 {
874 pkgPath: GOV_GOVERNANCE_PATH,
875 function: "UpgradeImpl",
876 paramCount: 1,
877 paramValidators: []paramValidator{
878 stringValidator, // targetPackagePath
879 },
880 handlerFunc: func(_ int, rlm realm, params []string) error {
881 // Upgrade governance implementation
882 governance.UpgradeImpl(cross(rlm), params[0]) // packagePath
883 return nil
884 },
885 },
886 {
887 pkgPath: GOV_STAKER_PATH,
888 function: "UpgradeImpl",
889 paramCount: 1,
890 paramValidators: []paramValidator{
891 stringValidator, // packagePath
892 },
893 handlerFunc: func(_ int, rlm realm, params []string) error {
894 // Upgrade gov staker implementation
895 gs.UpgradeImpl(cross(rlm), params[0]) // packagePath
896 return nil
897 },
898 },
899 {
900 pkgPath: ROUTER_PATH,
901 function: "UpgradeImpl",
902 paramCount: 1,
903 paramValidators: []paramValidator{
904 stringValidator, // packagePath
905 },
906 handlerFunc: func(_ int, rlm realm, params []string) error {
907 // Upgrade router implementation
908 rr.UpgradeImpl(cross(rlm), params[0]) // packagePath
909 return nil
910 },
911 },
912 {
913 pkgPath: PROTOCOL_FEE_PATH,
914 function: "UpgradeImpl",
915 paramCount: 1,
916 paramValidators: []paramValidator{
917 stringValidator, // packagePath
918 },
919 handlerFunc: func(_ int, rlm realm, params []string) error {
920 // Upgrade protocol fee implementation
921 pf.UpgradeImpl(cross(rlm), params[0]) // packagePath
922 return nil
923 },
924 },
925 {
926 pkgPath: COMMON_PATH,
927 function: "UpgradeImpl",
928 paramCount: 1,
929 paramValidators: []paramValidator{
930 stringValidator, // packagePath
931 },
932 handlerFunc: func(_ int, rlm realm, params []string) error {
933 // Upgrade common (token resolution) implementation
934 cm.UpgradeImpl(cross(rlm), params[0]) // packagePath
935 return nil
936 },
937 },
938 }
939
940 // Register all configured handlers in the registry
941 registerHandlers(registry, handlers)
942
943 return registry
944}
945
946// registerHandlers batch registers all configured handlers into the registry.
947// This helper function processes the handler configuration array and adds each handler to the registry.
948//
949// Parameters:
950// - registry: the parameter registry to add handlers to
951// - handlerOptions: slice of handler configurations to register
952func registerHandlers(registry *ParameterRegistry, handlerOptions []*ParameterHandlerOptions) {
953 for _, handlerOption := range handlerOptions {
954 registry.Register(*handlerOption)
955 }
956}
957
958// runValidator executes a validator function and converts panics to errors.
959func runValidator(fn func()) (err error) {
960 defer func() {
961 if r := recover(); r != nil {
962 if e, ok := r.(error); ok {
963 err = e
964 return
965 }
966 err = ufmt.Errorf("%v", r)
967 }
968 }()
969
970 fn()
971 return nil
972}
973
974// Basic reusable validators for proposal-time type checking.
975var (
976 stringValidator = func(s string) error {
977 return nil
978 }
979 boolValidator = func(s string) error {
980 return runValidator(func() {
981 parseBool(s)
982 })
983 }
984 uint64Validator = func(s string) error {
985 return runValidator(func() {
986 parseUint64(s)
987 })
988 }
989 addressValidator = func(s string) error {
990 return runValidator(func() {
991 addr := address(s)
992 if !addr.IsValid() {
993 panic(ufmt.Sprintf("invalid address: %s", addr))
994 }
995 })
996 }
997)
998
999func numberValidator(kind numberKind) paramValidator {
1000 return func(s string) error {
1001 return runValidator(func() {
1002 parseNumber(s, kind)
1003 })
1004 }
1005}
1006
1007func uint8RangeValidator(name string) paramValidator {
1008 return func(s string) error {
1009 return runValidator(func() {
1010 value := parseInt64(s)
1011 if value < 0 || value > 255 {
1012 panic(ufmt.Sprintf("%s out of range: %d", name, value))
1013 }
1014 })
1015 }
1016}
1017
1018func roleNameValidator(s string) error {
1019 return runValidator(func() {
1020 roleName := strings.TrimSpace(s)
1021 if roleName == "" {
1022 panic(ufmt.Sprintf("role name is empty: %q", s))
1023 }
1024 })
1025}
1026
1027func updatableRoleNameValidator(s string) error {
1028 return runValidator(func() {
1029 roleName := strings.TrimSpace(s)
1030 if err := roleNameValidator(roleName); err != nil {
1031 panic(err)
1032 }
1033
1034 if roleName == prbac.ROLE_ADMIN.String() {
1035 panic(ufmt.Sprintf("admin role cannot be updated: %s", roleName))
1036 }
1037 })
1038}
1039
1040func removableRoleNameValidator(s string) error {
1041 return runValidator(func() {
1042 roleName := strings.TrimSpace(s)
1043 if err := roleNameValidator(roleName); err != nil {
1044 panic(err)
1045 }
1046
1047 if prbac.IsSystemRole(roleName) {
1048 panic(ufmt.Sprintf("system role cannot be removed: %s", roleName))
1049 }
1050 })
1051}
1052
1053func nonNegativeInt64Validator(name string) paramValidator {
1054 return func(s string) error {
1055 return runValidator(func() {
1056 value := parseInt64(s)
1057 if value < 0 {
1058 panic(ufmt.Sprintf("%s must be non-negative: %d", name, value))
1059 }
1060 })
1061 }
1062}
1063
1064func positiveInt64Validator(name string) paramValidator {
1065 return func(s string) error {
1066 return runValidator(func() {
1067 value := parseInt64(s)
1068 if value <= 0 {
1069 panic(ufmt.Sprintf("%s must be positive: %d", name, value))
1070 }
1071 })
1072 }
1073}
1074
1075func int64RangeValidator(name string, min, max int64) paramValidator {
1076 return func(s string) error {
1077 return runValidator(func() {
1078 value := parseInt64(s)
1079 if value < min || value > max {
1080 panic(ufmt.Sprintf("%s must be in range %d to %d: %d", name, min, max, value))
1081 }
1082 })
1083 }
1084}
1085
1086func uint64RangeValidator(name string, max uint64) paramValidator {
1087 return func(s string) error {
1088 return runValidator(func() {
1089 value := parseUint64(s)
1090 if value > max {
1091 panic(ufmt.Sprintf("%s must be in range 0 to %d: %d", name, max, value))
1092 }
1093 })
1094 }
1095}
1096
1097func haltLevelValidator(s string) error {
1098 return runValidator(func() {
1099 if !halt.HaltLevel(s).IsValid() {
1100 panic(ufmt.Sprintf("invalid halt level: %s", s))
1101 }
1102 })
1103}
1104
1105func haltOperationTypeValidator(s string) error {
1106 return runValidator(func() {
1107 if !halt.OpType(s).IsValid() {
1108 panic(ufmt.Sprintf("invalid operation type: %s", s))
1109 }
1110 })
1111}
1112
1113func tokenMinimumRewardAmountValidator(s string) error {
1114 return runValidator(func() {
1115 parts := strings.SplitN(s, ":", 2)
1116 if len(parts) != 2 || parts[0] == "" {
1117 panic("token minimum reward amount must be tokenPath:amount")
1118 }
1119 amount := parseInt64(parts[1])
1120 if amount < 0 {
1121 panic(ufmt.Sprintf("minimum reward amount must be non-negative: %d", amount))
1122 }
1123 })
1124}
1125
1126func distributionPctCompositeValidator(params []string) error {
1127 return runValidator(func() {
1128 sum := int64(0)
1129 for i, param := range params {
1130 pct := parseInt64(param)
1131 if pct < 0 || pct > 10000 {
1132 panic(ufmt.Sprintf("percentage %d must be in range 0 to 10000: %d", i+1, pct))
1133 }
1134 sum += pct
1135 }
1136 if sum != 10000 {
1137 panic(ufmt.Sprintf("sum of percentages must be 10000, got %d", sum))
1138 }
1139 })
1140}
1141
1142func splitExecutionsRaw(executions string) []string {
1143 if executions == "" {
1144 return []string{}
1145 }
1146
1147 return strings.Split(executions, messageSeparator)
1148}
1149
1150func parseExecutionMessage(msg string) (pkgPath string, function string, params []string, partCount int) {
1151 parts := strings.Split(msg, parameterSeparator)
1152 partCount = len(parts)
1153 if partCount != 3 {
1154 return "", "", nil, partCount
1155 }
1156
1157 pkgPath = parts[0]
1158 function = parts[1]
1159 if parts[2] == "" {
1160 return pkgPath, function, []string{}, partCount
1161 }
1162
1163 return pkgPath, function, strings.Split(parts[2], ","), partCount
1164}
1165
1166// validateExecutions validates that all executions in a parameter change proposal
1167// correspond to registered handlers in the global parameter registry.
1168// This function performs comprehensive validation including:
1169// - Basic format validation (count, structure)
1170// - Handler existence verification in the registry
1171// - Parameter count validation against handler expectations
1172//
1173// Parameters:
1174// - numToExecute: number of parameter changes to execute
1175// - msgs: pre-split slice of execution messages, where each message
1176// is formatted as <pkgPath>*EXE*<function>*EXE*<params>
1177//
1178// Returns:
1179// - error: validation error if any execution is invalid
1180func validateExecutions(numToExecute int64, msgs []string) error {
1181 // Validate execution count is positive
1182 if numToExecute <= 0 {
1183 return makeErrorWithDetails(
1184 errInvalidInput,
1185 "numToExecute is less than or equal to 0",
1186 )
1187 }
1188
1189 // Check if executions are empty
1190 if len(msgs) == 0 {
1191 return makeErrorWithDetails(
1192 errInvalidInput,
1193 "executions is empty",
1194 )
1195 }
1196
1197 // Validate execution count doesn't exceed maximum
1198 if numToExecute > maxNumberOfExecution {
1199 return makeErrorWithDetails(
1200 errInvalidInput,
1201 ufmt.Sprintf("numToExecute is greater than %d", maxNumberOfExecution),
1202 )
1203 }
1204
1205 msgCount := len(msgs)
1206 // Validate execution count matches message count
1207 if msgCount != int(numToExecute) {
1208 return makeErrorWithDetails(
1209 errInvalidInput,
1210 ufmt.Sprintf("executions count (%d) does not match numToExecute (%d)", len(msgs), numToExecute),
1211 )
1212 }
1213
1214 // Parse and validate each execution message
1215 for i, execution := range msgs {
1216 pkgPath, function, params, partCount := parseExecutionMessage(execution)
1217 if partCount != 3 {
1218 // Provide more helpful error message based on what's wrong
1219 detail := ufmt.Sprintf("execution[%d]: expected 3 parts (pkgPath, function, params), got %d", i, partCount)
1220 return makeErrorWithDetails(
1221 errInvalidMessageFormat,
1222 detail,
1223 )
1224 }
1225
1226 // Validate package path and function are not empty
1227 if pkgPath == "" {
1228 return makeErrorWithDetails(
1229 errInvalidInput,
1230 ufmt.Sprintf("execution[%d]: package path is empty", i),
1231 )
1232 }
1233
1234 if function == "" {
1235 return makeErrorWithDetails(
1236 errInvalidInput,
1237 ufmt.Sprintf("execution[%d]: function name is empty", i),
1238 )
1239 }
1240
1241 // Check if handler exists in registry
1242 key := makeHandlerKey(pkgPath, function)
1243 handler, err := globalParameterRegistry.Handler(key)
1244 if err != nil {
1245 return makeErrorWithDetails(
1246 errInvalidExecution,
1247 ufmt.Sprintf("execution[%d]: %s (key: %s)", i, err.Error(), key),
1248 )
1249 }
1250
1251 // Get expected parameter count from handler
1252 handlerOpts, ok := handler.(*ParameterHandlerOptions)
1253 if !ok {
1254 return makeErrorWithDetails(
1255 errInvalidExecution,
1256 ufmt.Sprintf("execution[%d]: failed to get handler options", i),
1257 )
1258 }
1259
1260 // Validate parameter types/format ahead of proposal creation
1261 if err := handlerOpts.ValidateParams(params); err != nil {
1262 return makeErrorWithDetails(
1263 errInvalidInput,
1264 ufmt.Sprintf("execution[%d]: %v", i, err),
1265 )
1266 }
1267 }
1268
1269 return nil
1270}