package governance
import (
"strings"
prbac "gno.land/p/gnoswap/rbac"
ufmt "gno.land/p/nt/ufmt/v0"
cp "gno.land/r/gnoswap/community_pool"
en "gno.land/r/gnoswap/emission"
"gno.land/r/gnoswap/rbac"
"gno.land/r/gnoswap/gov/governance"
gs "gno.land/r/gnoswap/gov/staker"
lp "gno.land/r/gnoswap/launchpad"
pl "gno.land/r/gnoswap/pool"
pos "gno.land/r/gnoswap/position"
pf "gno.land/r/gnoswap/protocol_fee"
rr "gno.land/r/gnoswap/router"
sr "gno.land/r/gnoswap/staker"
"gno.land/r/gnoswap/halt"
)
// globalParameterRegistry is initialized once and reused for all validation and execution.
// This provides consistent validation at proposal creation time and execution time.
var globalParameterRegistry *ParameterRegistry
func init() {
globalParameterRegistry = CreateParameterHandlers()
}
// Package paths
const (
GNS_TOKEN_KEY = "gno.land/r/gnoswap/gns.GNS"
HALT_PATH = "gno.land/r/gnoswap/halt"
RBAC_PATH = "gno.land/r/gnoswap/rbac"
ACCESS_PATH = "gno.land/r/gnoswap/access"
EMISSION_PATH = "gno.land/r/gnoswap/emission"
COMMON_PATH = "gno.land/r/gnoswap/common"
POOL_PATH = "gno.land/r/gnoswap/pool"
POSITION_PATH = "gno.land/r/gnoswap/position"
ROUTER_PATH = "gno.land/r/gnoswap/router"
STAKER_PATH = "gno.land/r/gnoswap/staker"
LAUNCHPAD_PATH = "gno.land/r/gnoswap/launchpad"
PROTOCOL_FEE_PATH = "gno.land/r/gnoswap/protocol_fee"
COMMUNITY_POOL_PATH = "gno.land/r/gnoswap/community_pool"
GOV_GOVERNANCE_PATH = "gno.land/r/gnoswap/gov/governance"
GOV_STAKER_PATH = "gno.land/r/gnoswap/gov/staker"
)
// ParameterHandler interface defines the contract for parameter execution handlers.
// Each handler is responsible for executing specific parameter changes in the system.
type ParameterHandler interface {
// Execute processes the parameters and applies the changes to the system.
// The `_ int, cur realm` discriminator pair forwards the governance proxy's
// realm value into the handler so any cross-realm calls inside the closure
// run under the proxy's identity (the only address with caller-allowlist
// permission against the targeted /r/ realms).
Execute(_ int, rlm realm, params []string) error
}
// ParameterHandlerOptions contains the configuration and execution logic for a parameter handler.
// This struct encapsulates all information needed to identify and execute a parameter change.
//
// NOTE: handlerFunc uses `rlm realm` (rather than `cur realm`) as the realm
// parameter name. The v2 preprocessor reserves the `cur` name for the first
// realm-type parameter of top-level crossing function declarations and
// `t.Run` closures only; using it as the realm-parameter name on a
// multi-parameter struct-field function value trips a parser check
// ("only the first realm type argument of a crossing function may have name
// `cur`"). Naming it `rlm` keeps the lowering identical without the syntax
// constraint.
type ParameterHandlerOptions struct {
pkgPath string // Package path of the target contract
function string // Function name to be called
paramCount int // Expected number of parameters
handlerFunc func(_ int, rlm realm, _ []string) error // Function that executes the parameter change
paramValidators []paramValidator // Optional per-parameter validators for proposal-time checks
}
// paramValidator validates a single parameter value and returns an error on failure.
type paramValidator func(string) error
// NewParameterHandlerOptions creates a new parameter handler with the specified configuration.
//
// Parameters:
// - pkgPath: package path of the target contract
// - function: function name to be called
// - paramCount: expected number of parameters
// - handlerFunc: function that executes the parameter change
// - paramValidators: optional validators for each parameter (must match paramCount if provided)
//
// Returns:
// - ParameterHandler: configured parameter handler interface
func NewParameterHandlerOptions(
pkgPath,
function string,
paramCount int,
handlerFunc func(_ int, rlm realm, _ []string) error,
paramValidators ...paramValidator,
) ParameterHandler {
if len(paramValidators) > 0 && len(paramValidators) != paramCount {
panic(ufmt.Sprintf(
"invalid validator count for %s:%s: expected %d, got %d",
pkgPath, function, paramCount, len(paramValidators),
))
}
return &ParameterHandlerOptions{
pkgPath: pkgPath,
function: function,
paramCount: paramCount,
handlerFunc: handlerFunc,
paramValidators: paramValidators,
}
}
// HandlerKey generates a unique key for this handler based on package path and function name.
//
// Returns:
// - string: unique identifier for the handler
func (h *ParameterHandlerOptions) HandlerKey() string {
return makeHandlerKey(h.pkgPath, h.function)
}
// Execute validates parameter count and executes the handler function.
// This method ensures the correct number of parameters are provided before execution.
//
// Parameters:
// - cur: governance proxy realm threaded in by the caller; forwarded to the
// wrapped closure so its cross-realm calls run under the proxy.
// - params: slice of string parameters to pass to the handler
//
// Returns:
// - error: execution error if parameter count mismatch or handler execution fails
func (h *ParameterHandlerOptions) Execute(_ int, rlm realm, params []string) error {
// Validate parameter count matches expected count
if len(params) != h.paramCount {
return ufmt.Errorf("expected %d parameters, got %d", h.paramCount, len(params))
}
return h.handlerFunc(0, rlm, params)
}
// ValidateParams runs parameter count and per-parameter validation without executing the handler.
func (h *ParameterHandlerOptions) ValidateParams(params []string) error {
if len(params) != h.paramCount {
return ufmt.Errorf(
"expected %d parameters for %s, got %d",
h.paramCount, h.HandlerKey(), len(params),
)
}
if len(h.paramValidators) == 0 {
return nil
}
if len(h.paramValidators) != h.paramCount {
return ufmt.Errorf(
"validator count mismatch for %s: expected %d validator(s), got %d",
h.HandlerKey(), h.paramCount, len(h.paramValidators),
)
}
for i, validator := range h.paramValidators {
if validator == nil {
continue
}
if err := validator(params[i]); err != nil {
return ufmt.Errorf("param[%d]: %v", i, err)
}
}
return nil
}
// ParameterRegistry manages the collection of parameter handlers for governance execution.
// This registry allows proposals to execute parameter changes across different system contracts.
type ParameterRegistry struct {
handlers map[string]ParameterHandlerOptions // Map storing handler configurations keyed by package:function
}
// register adds a new parameter handler to the registry.
// Each handler is identified by a unique combination of package path and function name.
//
// Parameters:
// - handler: parameter handler configuration to register
func (r *ParameterRegistry) Register(handler ParameterHandlerOptions) {
r.handlers[handler.HandlerKey()] = handler
}
// handler retrieves a parameter handler by package path and function name.
// This method is used during proposal execution to find the appropriate handler.
//
// Parameters:
// - pkgPath: package path of the target contract
// - function: function name to be called
//
// Returns:
// - ParameterHandler: the matching parameter handler
// - error: error if handler not found or casting fails
func (r *ParameterRegistry) Handler(key string) (ParameterHandler, error) {
// Retrieve handler from registry
handler, exists := r.handlers[key]
if !exists {
return nil, ufmt.Errorf("handler not found for %s", key)
}
return &handler, nil
}
// NewParameterRegistry creates a new empty parameter registry.
//
// Returns:
// - *ParameterRegistry: new registry instance
func NewParameterRegistry() *ParameterRegistry {
return &ParameterRegistry{handlers: make(map[string]ParameterHandlerOptions)}
}
// makeHandlerKey creates a unique identifier for a handler based on package path and function.
//
// Parameters:
// - pkgPath: package path of the target contract
// - function: function name to be called
//
// Returns:
// - string: unique key in format "pkgPath:function"
func makeHandlerKey(pkgPath, function string) string {
return pkgPath + ":" + function
}
// createParameterHandlers initializes and configures all supported parameter handlers.
// This function defines all the parameter changes that can be executed through governance proposals.
// It covers configuration changes for various system components including pools, staking, fees, etc.
//
// Returns:
// - *ParameterRegistry: fully configured registry with all supported handlers
func CreateParameterHandlers() *ParameterRegistry {
registry := NewParameterRegistry()
// Define all handler configurations for different system components.
//
// Each closure receives `(_ int, rlm realm, params []string)` and threads
// `rlm` into the targeted /r/ realm call. The closure is invoked from
// ParameterHandlerOptions.Execute which forwards the governance proxy's
// realm value, so cross-realm calls land on the targeted realm with the
// governance proxy as the immediate caller.
handlers := []*ParameterHandlerOptions{
// Community pool token transfers
{
pkgPath: COMMUNITY_POOL_PATH,
function: "TransferToken",
paramCount: 3,
paramValidators: []paramValidator{
stringValidator, // pkgPath
addressValidator, // to
numberValidator(kindInt64), // amount
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Transfer tokens from community pool to specified address
cp.TransferToken(
cross(rlm),
params[0], // pkgPath
address(params[1]), // to
parseNumber(params[2], kindInt64).(int64), // amount
)
return nil
},
},
// Emission distribution configuration
{
pkgPath: EMISSION_PATH,
function: "SetDistributionStartTime",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // start time
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set distribution start time
en.SetDistributionStartTime(cross(rlm), parseInt64(params[0]))
return nil
},
},
{
pkgPath: EMISSION_PATH,
function: "ChangeDistributionPct",
paramCount: 4,
paramValidators: []paramValidator{
numberValidator(kindInt64), // liquidityStakerPct
numberValidator(kindInt64), // devOpsPct
numberValidator(kindInt64), // communityPoolPct
numberValidator(kindInt64), // govStakerPct
},
handlerFunc: func(_ int, rlm realm, params []string) error {
liquidityStakerPct := parseNumber(params[0], kindInt64).(int64)
devOpsPct := parseNumber(params[1], kindInt64).(int64)
communityPoolPct := parseNumber(params[2], kindInt64).(int64)
govStakerPct := parseNumber(params[3], kindInt64).(int64)
en.ChangeDistributionPct(
cross(rlm),
liquidityStakerPct,
devOpsPct,
communityPoolPct,
govStakerPct,
)
return nil
},
},
// Governance configuration changes
{
pkgPath: GOV_GOVERNANCE_PATH,
function: "Reconfigure",
paramCount: 7,
paramValidators: []paramValidator{
numberValidator(kindInt64), // votingStartDelay
numberValidator(kindInt64), // votingPeriod
numberValidator(kindInt64), // votingWeightSmoothingDuration
numberValidator(kindInt64), // quorum
numberValidator(kindInt64), // proposalCreationThreshold
numberValidator(kindInt64), // executionDelay
numberValidator(kindInt64), // executionWindow
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Parse governance configuration parameters
votingStartDelay := parseInt64(params[0])
votingPeriod := parseInt64(params[1])
votingWeightSmoothingDuration := parseInt64(params[2])
quorum := parseInt64(params[3])
proposalCreationThreshold := parseInt64(params[4])
executionDelay := parseInt64(params[5])
executionWindow := parseInt64(params[6])
// Reconfigure governance parameters through governance process
governance.Reconfigure(
cross(rlm),
votingStartDelay,
votingPeriod,
votingWeightSmoothingDuration,
quorum,
proposalCreationThreshold,
executionDelay,
executionWindow,
)
return nil
},
},
// Pool protocol fee configuration
{
pkgPath: POOL_PATH,
function: "CollectProtocol",
paramCount: 6,
paramValidators: []paramValidator{
stringValidator, // token0Path
stringValidator, // token1Path
uint64Validator, // fee
addressValidator, // recipient
nonNegativeInt64Validator("amount0Requested"),
nonNegativeInt64Validator("amount1Requested"),
},
handlerFunc: func(_ int, rlm realm, params []string) error {
pl.CollectProtocol(
cross(rlm),
params[0], // token0Path
params[1], // token1Path
uint32(parseUint64(params[2])), // fee
address(params[3]), // recipient
params[4], // amount0Requested
params[5], // amount1Requested
)
return nil
},
},
{
pkgPath: POOL_PATH,
function: "SetFeeProtocol",
paramCount: 2,
paramValidators: []paramValidator{
uint8RangeValidator("feeProtocol0"),
uint8RangeValidator("feeProtocol1"),
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Parse and validate fee protocol values
feeProtocol0 := parseInt64(params[0])
feeProtocol1 := parseInt64(params[1])
// Validate fee protocol values are within uint8 range
if feeProtocol0 > 255 {
panic(ufmt.Sprintf("feeProtocol0 out of range: %d", feeProtocol0))
}
if feeProtocol1 > 255 {
panic(ufmt.Sprintf("feeProtocol1 out of range: %d", feeProtocol1))
}
// Set protocol fee percentages
pl.SetFeeProtocol(
cross(rlm),
uint8(feeProtocol0), // feeProtocol0
uint8(feeProtocol1), // feeProtocol1
)
return nil
},
},
// Pool creation fee
{
pkgPath: POOL_PATH,
function: "SetPoolCreationFee",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // fee
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set fee required to create new pools
pl.SetPoolCreationFee(cross(rlm), parseInt64(params[0])) // fee
return nil
},
},
// Pool withdrawal fee
{
pkgPath: POOL_PATH,
function: "SetWithdrawalFee",
paramCount: 1,
paramValidators: []paramValidator{
uint64Validator, // fee
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set fee for withdrawing from pools
pl.SetWithdrawalFee(cross(rlm), parseUint64(params[0])) // fee
return nil
},
},
// Protocol fee distribution
{
pkgPath: PROTOCOL_FEE_PATH,
function: "SetDevOpsPct",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // pct
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set percentage of protocol fees going to development operations
pf.SetDevOpsPct(cross(rlm), parseInt64(params[0])) // pct
return nil
},
},
// Router swap fee
{
pkgPath: ROUTER_PATH,
function: "SetSwapFee",
paramCount: 1,
paramValidators: []paramValidator{
uint64Validator, // fee
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set fee charged for token swaps
rr.SetSwapFee(cross(rlm), parseUint64(params[0])) // fee
return nil
},
},
// Staker configuration handlers
{
pkgPath: STAKER_PATH,
function: "SetDepositGnsAmount",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // amount
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set minimum GNS amount required for staking deposits
sr.SetDepositGnsAmount(cross(rlm), parseInt64(params[0])) // amount
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "SetMinimumRewardAmount",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // amount
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set minimum GNS amount required for staking deposits
sr.SetMinimumRewardAmount(cross(rlm), parseInt64(params[0])) // amount
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "SetTokenMinimumRewardAmount",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // tokenPath:amount
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set minimum GNS amount required for staking deposits
// params[0] is a string in the format "tokenPath:amount"
sr.SetTokenMinimumRewardAmount(cross(rlm), params[0]) // amount
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "SetPoolTier",
paramCount: 2,
paramValidators: []paramValidator{
stringValidator, // pool
uint64Validator, // tier
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Assign tier level to a specific pool
sr.SetPoolTier(
cross(rlm),
params[0], // pool
parseUint64(params[1]), // tier
)
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "ChangePoolTier",
paramCount: 2,
paramValidators: []paramValidator{
stringValidator, // pool
uint64Validator, // tier
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Change existing pool's tier level
sr.ChangePoolTier(
cross(rlm),
params[0], // pool
parseUint64(params[1]), // tier
)
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "RemovePoolTier",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // pool
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Remove tier assignment from a pool
sr.RemovePoolTier(cross(rlm), params[0]) // pool
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "SetUnStakingFee",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindUint64), // fee
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set fee charged for unstaking operations
fee := parseUint64(params[0])
sr.SetUnStakingFee(cross(rlm), fee)
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "SetWarmUp",
paramCount: 2,
paramValidators: []paramValidator{
numberValidator(kindInt64), // percent
numberValidator(kindInt64), // block
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set warm-up period configuration for staking
percent := parseInt64(params[0])
block := parseNumber(params[1], kindInt64).(int64)
sr.SetWarmUp(cross(rlm), percent, block)
return nil
},
},
// System halt controls
{
pkgPath: HALT_PATH,
function: "SetHaltLevel",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // halt level string
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set system-wide halt status
halt.SetHaltLevel(cross(rlm), halt.HaltLevel(params[0])) // true = halt, false = no halt
return nil
},
},
{
pkgPath: HALT_PATH,
function: "SetOperationStatus",
paramCount: 2,
paramValidators: []paramValidator{
stringValidator, // opType
boolValidator, // allowed
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Enable or disable specific operation types
opType := halt.OpType(params[0])
allowed := parseBool(params[1])
halt.SetOperationStatus(cross(rlm), opType, allowed)
return nil
},
},
// RBAC configuration
{
pkgPath: RBAC_PATH,
function: "RegisterRole",
paramCount: 2,
paramValidators: []paramValidator{
roleNameValidator,
addressValidator, // roleAddress
},
handlerFunc: func(_ int, rlm realm, params []string) error {
roleName := params[0]
roleAddress := address(params[1])
// Register a new role
rbac.RegisterRole(cross(rlm), roleName, roleAddress)
return nil
},
},
{
pkgPath: RBAC_PATH,
function: "UpdateRoleAddress",
paramCount: 2,
paramValidators: []paramValidator{
updatableRoleNameValidator,
addressValidator, // roleAddress
},
handlerFunc: func(_ int, rlm realm, params []string) error {
roleName := params[0]
roleAddress := address(params[1])
// Update role address
rbac.UpdateRoleAddress(cross(rlm), roleName, roleAddress)
return nil
},
},
{
pkgPath: RBAC_PATH,
function: "RemoveRole",
paramCount: 1,
paramValidators: []paramValidator{
removableRoleNameValidator,
},
handlerFunc: func(_ int, rlm realm, params []string) error {
roleName := params[0]
// Remove role
rbac.RemoveRole(cross(rlm), roleName)
return nil
},
},
// Protocol fee - SetGovStakerPct
{
pkgPath: PROTOCOL_FEE_PATH,
function: "SetGovStakerPct",
paramCount: 1,
paramValidators: []paramValidator{
numberValidator(kindInt64), // pct
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Set percentage of protocol fees going to governance stakers
pf.SetGovStakerPct(cross(rlm), parseInt64(params[0])) // pct
return nil
},
},
// Staker - Token list management
{
pkgPath: STAKER_PATH,
function: "AddToken",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // tokenPath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Add token to allowed token list for external incentives
sr.AddToken(cross(rlm), params[0]) // tokenPath
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "RemoveToken",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // tokenPath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Remove token from allowed token list
sr.RemoveToken(cross(rlm), params[0]) // tokenPath
return nil
},
},
// Launchpad - Project management
{
pkgPath: LAUNCHPAD_PATH,
function: "CreateProject",
paramCount: 10,
paramValidators: []paramValidator{
stringValidator, // name
stringValidator, // tokenPath
addressValidator, // recipient
numberValidator(kindInt64), // depositAmount
stringValidator, // conditionTokens
stringValidator, // conditionAmounts
numberValidator(kindInt64), // tier30Ratio
numberValidator(kindInt64), // tier90Ratio
numberValidator(kindInt64), // tier180Ratio
numberValidator(kindInt64), // startTime
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Create a new launchpad project
lp.CreateProject(
cross(rlm),
params[0], // name
params[1], // tokenPath
address(params[2]), // recipient
parseNumber(params[3], kindInt64).(int64), // depositAmount
params[4], // conditionTokens
params[5], // conditionAmounts
parseNumber(params[6], kindInt64).(int64), // tier30Ratio
parseNumber(params[7], kindInt64).(int64), // tier90Ratio
parseNumber(params[8], kindInt64).(int64), // tier180Ratio
parseNumber(params[9], kindInt64).(int64), // startTime
)
return nil
},
},
// Upgrade handlers for various domains
{
pkgPath: POOL_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade pool implementation
pl.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: POSITION_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade position implementation
pos.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: STAKER_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade staker implementation
sr.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: LAUNCHPAD_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade launchpad implementation
lp.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: GOV_GOVERNANCE_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // targetPackagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade governance implementation
governance.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: GOV_STAKER_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade gov staker implementation
gs.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: ROUTER_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade router implementation
rr.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
{
pkgPath: PROTOCOL_FEE_PATH,
function: "UpgradeImpl",
paramCount: 1,
paramValidators: []paramValidator{
stringValidator, // packagePath
},
handlerFunc: func(_ int, rlm realm, params []string) error {
// Upgrade protocol fee implementation
pf.UpgradeImpl(cross(rlm), params[0]) // packagePath
return nil
},
},
}
// Register all configured handlers in the registry
registerHandlers(registry, handlers)
return registry
}
// registerHandlers batch registers all configured handlers into the registry.
// This helper function processes the handler configuration array and adds each handler to the registry.
//
// Parameters:
// - registry: the parameter registry to add handlers to
// - handlerOptions: slice of handler configurations to register
func registerHandlers(registry *ParameterRegistry, handlerOptions []*ParameterHandlerOptions) {
for _, handlerOption := range handlerOptions {
registry.Register(*handlerOption)
}
}
// runValidator executes a validator function and converts panics to errors.
func runValidator(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
if e, ok := r.(error); ok {
err = e
return
}
err = ufmt.Errorf("%v", r)
}
}()
fn()
return nil
}
// Basic reusable validators for proposal-time type checking.
var (
stringValidator = func(s string) error {
return nil
}
boolValidator = func(s string) error {
return runValidator(func() {
parseBool(s)
})
}
uint64Validator = func(s string) error {
return runValidator(func() {
parseUint64(s)
})
}
addressValidator = func(s string) error {
return runValidator(func() {
addr := address(s)
if !addr.IsValid() {
panic(ufmt.Sprintf("invalid address: %s", addr))
}
})
}
)
func numberValidator(kind numberKind) paramValidator {
return func(s string) error {
return runValidator(func() {
parseNumber(s, kind)
})
}
}
func uint8RangeValidator(name string) paramValidator {
return func(s string) error {
return runValidator(func() {
value := parseInt64(s)
if value < 0 || value > 255 {
panic(ufmt.Sprintf("%s out of range: %d", name, value))
}
})
}
}
func roleNameValidator(s string) error {
return runValidator(func() {
roleName := strings.TrimSpace(s)
if roleName == "" {
panic(ufmt.Sprintf("role name is empty: %q", s))
}
})
}
func updatableRoleNameValidator(s string) error {
return runValidator(func() {
roleName := strings.TrimSpace(s)
if err := roleNameValidator(roleName); err != nil {
panic(err)
}
if roleName == prbac.ROLE_ADMIN.String() {
panic(ufmt.Sprintf("admin role cannot be updated: %s", roleName))
}
})
}
func removableRoleNameValidator(s string) error {
return runValidator(func() {
roleName := strings.TrimSpace(s)
if err := roleNameValidator(roleName); err != nil {
panic(err)
}
if prbac.IsSystemRole(roleName) {
panic(ufmt.Sprintf("system role cannot be removed: %s", roleName))
}
})
}
func nonNegativeInt64Validator(name string) paramValidator {
return func(s string) error {
return runValidator(func() {
value := parseInt64(s)
if value < 0 {
panic(ufmt.Sprintf("%s must be non-negative: %d", name, value))
}
})
}
}
func splitExecutionsRaw(executions string) []string {
if executions == "" {
return []string{}
}
return strings.Split(executions, messageSeparator)
}
func parseExecutionMessage(msg string) (pkgPath string, function string, params []string, partCount int) {
parts := strings.Split(msg, parameterSeparator)
partCount = len(parts)
if partCount != 3 {
return "", "", nil, partCount
}
pkgPath = parts[0]
function = parts[1]
if parts[2] == "" {
return pkgPath, function, []string{}, partCount
}
return pkgPath, function, strings.Split(parts[2], ","), partCount
}
// validateExecutions validates that all executions in a parameter change proposal
// correspond to registered handlers in the global parameter registry.
// This function performs comprehensive validation including:
// - Basic format validation (count, structure)
// - Handler existence verification in the registry
// - Parameter count validation against handler expectations
//
// Parameters:
// - numToExecute: number of parameter changes to execute
// - msgs: pre-split slice of execution messages, where each message
// is formatted as <pkgPath>*EXE*<function>*EXE*<params>
//
// Returns:
// - error: validation error if any execution is invalid
func validateExecutions(numToExecute int64, msgs []string) error {
// Validate execution count is positive
if numToExecute <= 0 {
return makeErrorWithDetails(
errInvalidInput,
"numToExecute is less than or equal to 0",
)
}
// Check if executions are empty
if len(msgs) == 0 {
return makeErrorWithDetails(
errInvalidInput,
"executions is empty",
)
}
// Validate execution count doesn't exceed maximum
if numToExecute > maxNumberOfExecution {
return makeErrorWithDetails(
errInvalidInput,
ufmt.Sprintf("numToExecute is greater than %d", maxNumberOfExecution),
)
}
msgCount := len(msgs)
// Validate execution count matches message count
if msgCount != int(numToExecute) {
return makeErrorWithDetails(
errInvalidInput,
ufmt.Sprintf("executions count (%d) does not match numToExecute (%d)", len(msgs), numToExecute),
)
}
// Parse and validate each execution message
for i, execution := range msgs {
pkgPath, function, params, partCount := parseExecutionMessage(execution)
if partCount != 3 {
// Provide more helpful error message based on what's wrong
detail := ufmt.Sprintf("execution[%d]: expected 3 parts (pkgPath, function, params), got %d", i, partCount)
return makeErrorWithDetails(
errInvalidMessageFormat,
detail,
)
}
// Validate package path and function are not empty
if pkgPath == "" {
return makeErrorWithDetails(
errInvalidInput,
ufmt.Sprintf("execution[%d]: package path is empty", i),
)
}
if function == "" {
return makeErrorWithDetails(
errInvalidInput,
ufmt.Sprintf("execution[%d]: function name is empty", i),
)
}
// Check if handler exists in registry
key := makeHandlerKey(pkgPath, function)
handler, err := globalParameterRegistry.Handler(key)
if err != nil {
return makeErrorWithDetails(
errInvalidExecution,
ufmt.Sprintf("execution[%d]: %s (key: %s)", i, err.Error(), key),
)
}
// Get expected parameter count from handler
handlerOpts, ok := handler.(*ParameterHandlerOptions)
if !ok {
return makeErrorWithDetails(
errInvalidExecution,
ufmt.Sprintf("execution[%d]: failed to get handler options", i),
)
}
// Validate parameter types/format ahead of proposal creation
if err := handlerOpts.ValidateParams(params); err != nil {
return makeErrorWithDetails(
errInvalidInput,
ufmt.Sprintf("execution[%d]: %v", i, err),
)
}
}
return nil
}