Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}