package v1 import ( "strings" "gno.land/p/nt/avl" "gno.land/p/nt/ufmt" 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_PATH = "gno.land/r/gnoswap/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 Execute(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. 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([]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([]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: // - 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(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(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 *avl.Tree // Tree 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.Set(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 h, exists := r.handlers.Get(key) if !exists { return nil, ufmt.Errorf("handler not found for %s", key) } // Cast to correct type handler, ok := h.(ParameterHandlerOptions) if !ok { return nil, ufmt.Errorf("failed to cast handler %s to ParameterHandler", key) } return &handler, nil } // NewParameterRegistry creates a new empty parameter registry. // // Returns: // - *ParameterRegistry: new registry instance func NewParameterRegistry() *ParameterRegistry { return &ParameterRegistry{handlers: avl.NewTree()} } // 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 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(params []string) error { // Transfer tokens from community pool to specified address cp.TransferToken( cross, 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(params []string) error { // Set distribution start time en.SetDistributionStartTime(cross, parseInt64(params[0])) return nil }, }, { pkgPath: EMISSION_PATH, function: "ChangeDistributionPct", paramCount: 8, paramValidators: []paramValidator{ numberValidator(kindInt), // target01 numberValidator(kindInt64), // pct01 numberValidator(kindInt), // target02 numberValidator(kindInt64), // pct02 numberValidator(kindInt), // target03 numberValidator(kindInt64), // pct03 numberValidator(kindInt), // target04 numberValidator(kindInt64), // pct04 }, handlerFunc: func(params []string) error { // Parse distribution targets and percentages target01 := parseNumber(params[0], kindInt).(int) // target01 pct01 := parseNumber(params[1], kindInt64).(int64) // pct01 target02 := parseNumber(params[2], kindInt).(int) // target02 pct02 := parseNumber(params[3], kindInt64).(int64) // pct02 target03 := parseNumber(params[4], kindInt).(int) // target03 pct03 := parseNumber(params[5], kindInt64).(int64) // pct03 target04 := parseNumber(params[6], kindInt).(int) // target04 pct04 := parseNumber(params[7], kindInt64).(int64) // pct04 // Update emission distribution percentages en.ChangeDistributionPct( cross, target01, // target01 pct01, // pct01 target02, // target02 pct02, // pct02 target03, // target03 pct03, // pct03 target04, // target04 pct04, // pct04 ) 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(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, 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 nil, // amount0Requested (validated in pool logic) nil, // amount1Requested (validated in pool logic) }, handlerFunc: func(params []string) error { pl.CollectProtocol( cross, 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(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, 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(params []string) error { // Set fee required to create new pools pl.SetPoolCreationFee(cross, parseInt64(params[0])) // fee return nil }, }, // Pool withdrawal fee { pkgPath: POOL_PATH, function: "SetWithdrawalFee", paramCount: 1, paramValidators: []paramValidator{ uint64Validator, // fee }, handlerFunc: func(params []string) error { // Set fee for withdrawing from pools pl.SetWithdrawalFee(cross, parseUint64(params[0])) // fee return nil }, }, // Protocol fee distribution { pkgPath: PROTOCOL_FEE_PATH, function: "SetDevOpsPct", paramCount: 1, paramValidators: []paramValidator{ numberValidator(kindInt64), // pct }, handlerFunc: func(params []string) error { // Set percentage of protocol fees going to development operations pf.SetDevOpsPct(cross, parseInt64(params[0])) // pct return nil }, }, // Router swap fee { pkgPath: ROUTER_PATH, function: "SetSwapFee", paramCount: 1, paramValidators: []paramValidator{ uint64Validator, // fee }, handlerFunc: func(params []string) error { // Set fee charged for token swaps rr.SetSwapFee(cross, parseUint64(params[0])) // fee return nil }, }, // Staker configuration handlers { pkgPath: STAKER_PATH, function: "SetDepositGnsAmount", paramCount: 1, paramValidators: []paramValidator{ numberValidator(kindInt64), // amount }, handlerFunc: func(params []string) error { // Set minimum GNS amount required for staking deposits sr.SetDepositGnsAmount(cross, parseInt64(params[0])) // amount return nil }, }, { pkgPath: STAKER_PATH, function: "SetMinimumRewardAmount", paramCount: 1, paramValidators: []paramValidator{ numberValidator(kindInt64), // amount }, handlerFunc: func(params []string) error { // Set minimum GNS amount required for staking deposits sr.SetMinimumRewardAmount(cross, parseInt64(params[0])) // amount return nil }, }, { pkgPath: STAKER_PATH, function: "SetTokenMinimumRewardAmount", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // tokenPath:amount }, handlerFunc: func(params []string) error { // Set minimum GNS amount required for staking deposits // params[0] is a string in the format "tokenPath:amount" sr.SetTokenMinimumRewardAmount(cross, params[0]) // amount return nil }, }, { pkgPath: STAKER_PATH, function: "SetPoolTier", paramCount: 2, paramValidators: []paramValidator{ stringValidator, // pool uint64Validator, // tier }, handlerFunc: func(params []string) error { // Assign tier level to a specific pool sr.SetPoolTier( cross, params[0], // pool parseUint64(params[1]), // tier ) return nil }, }, { pkgPath: STAKER_PATH, function: "ChangePoolTier", paramCount: 2, paramValidators: []paramValidator{ stringValidator, // pool uint64Validator, // tier }, handlerFunc: func(params []string) error { // Change existing pool's tier level sr.ChangePoolTier( cross, params[0], // pool parseUint64(params[1]), // tier ) return nil }, }, { pkgPath: STAKER_PATH, function: "RemovePoolTier", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // pool }, handlerFunc: func(params []string) error { // Remove tier assignment from a pool sr.RemovePoolTier(cross, params[0]) // pool return nil }, }, { pkgPath: STAKER_PATH, function: "SetUnStakingFee", paramCount: 1, paramValidators: []paramValidator{ numberValidator(kindInt64), // fee }, handlerFunc: func(params []string) error { // Set fee charged for unstaking operations fee := parseInt64(params[0]) sr.SetUnStakingFee(cross, fee) return nil }, }, { pkgPath: STAKER_PATH, function: "SetWarmUp", paramCount: 2, paramValidators: []paramValidator{ numberValidator(kindInt64), // percent numberValidator(kindInt64), // block }, handlerFunc: func(params []string) error { // Set warm-up period configuration for staking percent := parseInt64(params[0]) block := parseNumber(params[1], kindInt64).(int64) sr.SetWarmUp(cross, percent, block) return nil }, }, // System halt controls { pkgPath: HALT_PATH, function: "SetHaltLevel", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // halt level string }, handlerFunc: func(params []string) error { // Set system-wide halt status halt.SetHaltLevel(cross, 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(params []string) error { // Enable or disable specific operation types opType := halt.OpType(params[0]) allowed := parseBool(params[1]) halt.SetOperationStatus(cross, opType, allowed) return nil }, }, // RBAC configuration { pkgPath: RBAC_PATH, function: "RegisterRole", paramCount: 2, paramValidators: []paramValidator{ stringValidator, // roleName addressValidator, // roleAddress }, handlerFunc: func(params []string) error { roleName := params[0] roleAddress := address(params[1]) // Register a new role rbac.RegisterRole(cross, roleName, roleAddress) return nil }, }, { pkgPath: RBAC_PATH, function: "UpdateRoleAddress", paramCount: 2, paramValidators: []paramValidator{ stringValidator, // roleName addressValidator, // roleAddress }, handlerFunc: func(params []string) error { roleName := params[0] roleAddress := address(params[1]) // Update role address rbac.UpdateRoleAddress(cross, roleName, roleAddress) return nil }, }, { pkgPath: RBAC_PATH, function: "RemoveRole", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // roleName }, handlerFunc: func(params []string) error { roleName := params[0] // Remove role rbac.RemoveRole(cross, roleName) return nil }, }, // Protocol fee - SetGovStakerPct { pkgPath: PROTOCOL_FEE_PATH, function: "SetGovStakerPct", paramCount: 1, paramValidators: []paramValidator{ numberValidator(kindInt64), // pct }, handlerFunc: func(params []string) error { // Set percentage of protocol fees going to governance stakers pf.SetGovStakerPct(cross, parseInt64(params[0])) // pct return nil }, }, // Staker - Token list management { pkgPath: STAKER_PATH, function: "AddToken", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // tokenPath }, handlerFunc: func(params []string) error { // Add token to allowed token list for external incentives sr.AddToken(cross, params[0]) // tokenPath return nil }, }, { pkgPath: STAKER_PATH, function: "RemoveToken", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // tokenPath }, handlerFunc: func(params []string) error { // Remove token from allowed token list sr.RemoveToken(cross, 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(params []string) error { // Create a new launchpad project lp.CreateProject( cross, 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(params []string) error { // Upgrade pool implementation pl.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: POSITION_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade position implementation pos.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: STAKER_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade staker implementation sr.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: LAUNCHPAD_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade launchpad implementation lp.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: GOV_GOVERNANCE_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // targetPackagePath }, handlerFunc: func(params []string) error { // Upgrade governance implementation governance.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: GOV_STAKER_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade gov staker implementation gs.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: ROUTER_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade router implementation rr.UpgradeImpl(cross, params[0]) // packagePath return nil }, }, { pkgPath: PROTOCOL_FEE_PATH, function: "UpgradeImpl", paramCount: 1, paramValidators: []paramValidator{ stringValidator, // packagePath }, handlerFunc: func(params []string) error { // Upgrade protocol fee implementation pf.UpgradeImpl(cross, 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 { // allow zero address if s == "" { return nil } 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)) } }) } } // 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 // - executions: encoded execution string with parameter changes // // Returns: // - error: validation error if any execution is invalid func validateExecutions(numToExecute int64, executions string) error { // Validate execution count is positive if numToExecute <= 0 { return makeErrorWithDetails( errInvalidInput, "numToExecute is less than or equal to 0", ) } // Check if executions string is empty if executions == "" { return makeErrorWithDetails( errInvalidInput, "executions is empty", ) } // Split execution string into individual messages msgs := strings.Split(executions, messageSeparator) 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), ) } // Validate execution count doesn't exceed maximum if numToExecute > maxNumberOfExecution { return makeErrorWithDetails( errInvalidInput, ufmt.Sprintf("numToExecute is greater than %d", maxNumberOfExecution), ) } // Parse and validate each execution message for i, msg := range msgs { // Split message into components: pkgPath, function, params parts := strings.Split(msg, parameterSeparator) if len(parts) != 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, len(parts)) return makeErrorWithDetails( errInvalidMessageFormat, detail, ) } pkgPath := parts[0] function := parts[1] paramStr := parts[2] // 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), ) } // Validate parameter count matches handler's expected count params := []string{} if paramStr != "" { params = strings.Split(paramStr, ",") } // 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 }