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