config.gno

package governance

import (
	"errors"

	bptree "gno.land/p/nt/bptree/v0"
)

// Config represents the configuration of the governor contract
// All parameters in this struct can be modified through governance.
type Config struct {
	// VotingStartDelay is the delay before voting starts after proposal creation (in seconds)
	VotingStartDelay int64
	// VotingPeriod is the duration during which votes are collected (in seconds)
	VotingPeriod int64
	// VotingWeightSmoothingDuration is the period over which voting weight is averaged
	// for proposal creation and cancellation threshold calculations (in seconds)
	VotingWeightSmoothingDuration int64
	// Quorum is the percentage of active xGNS supply required for proposal approval
	Quorum int64
	// ProposalCreationThreshold is the minimum xGNS amount required to create a proposal
	ProposalCreationThreshold int64
	// ExecutionDelay is the waiting period after voting ends before a proposal can be executed (in seconds)
	ExecutionDelay int64
	// ExecutionWindow is the time window during which an approved proposal can be executed (in seconds)
	ExecutionWindow int64
}

func (c Config) IsValid(currentTime int64) error {
	if c.VotingStartDelay < 0 {
		return errors.New("votingStartDelay cannot be negative")
	}

	if c.VotingPeriod < 0 {
		return errors.New("votingPeriod cannot be negative")
	}

	if c.ExecutionDelay < 0 {
		return errors.New("executionDelay cannot be negative")
	}

	if c.ExecutionWindow < 0 {
		return errors.New("executionWindow cannot be negative")
	}

	if c.VotingWeightSmoothingDuration < 0 {
		return errors.New("votingWeightSmoothingDuration cannot be negative")
	}

	if c.ProposalCreationThreshold < 0 {
		return errors.New("proposalCreationThreshold cannot be negative")
	}

	if c.Quorum < 0 || c.Quorum > 100 {
		return errors.New("quorum must be between 0 and 100")
	}

	sum := int64(0)

	sum += c.VotingStartDelay
	if sum < 0 {
		return errors.New("votingStartDelay cannot be negative")
	}

	sum += c.VotingPeriod
	if sum < 0 {
		return errors.New("votingPeriod cannot be negative")
	}

	sum += c.ExecutionDelay
	if sum < 0 {
		return errors.New("executionDelay cannot be negative")
	}

	sum += c.ExecutionWindow
	if sum < 0 {
		return errors.New("executionWindow cannot be negative")
	}

	sum += currentTime
	if sum < 0 {
		return errors.New("total delay cannot be negative")
	}

	return nil
}

func NewConfig(
	votingStartDelay,
	votingPeriod,
	votingWeightSmoothingDuration,
	quorum,
	proposalCreationThreshold,
	executionDelay,
	executionWindow int64,
) Config {
	return Config{
		VotingStartDelay:              votingStartDelay,
		VotingPeriod:                  votingPeriod,
		VotingWeightSmoothingDuration: votingWeightSmoothingDuration,
		Quorum:                        quorum,
		ProposalCreationThreshold:     proposalCreationThreshold,
		ExecutionDelay:                executionDelay,
		ExecutionWindow:               executionWindow,
	}
}

// NewConfigPtr constructs a Config and returns a pointer to it. The Config is
// allocated within the governance domain realm, satisfying realm allocation
// checks for callers (such as tests) that require a *Config value.
func NewConfigPtr(
	votingStartDelay,
	votingPeriod,
	votingWeightSmoothingDuration,
	quorum,
	proposalCreationThreshold,
	executionDelay,
	executionWindow int64,
) *Config {
	c := NewConfig(
		votingStartDelay,
		votingPeriod,
		votingWeightSmoothingDuration,
		quorum,
		proposalCreationThreshold,
		executionDelay,
		executionWindow,
	)
	return &c
}

func NewDefaultConfig() Config {
	return Config{
		VotingStartDelay:              86400,         // 1 day - delay before voting starts
		VotingPeriod:                  604800,        // 7 days - duration for collecting votes
		VotingWeightSmoothingDuration: 86400,         // 1 day - period for averaging voting weight
		Quorum:                        50,            // 50% of total xGNS supply required
		ProposalCreationThreshold:     1_000_000_000, // 1 billion xGNS - minimum amount to create proposals
		ExecutionDelay:                86400,         // 1 day - waiting period before execution
		ExecutionWindow:               2592000,       // 30 days - window for executing proposals
	}
}

func NewConfigTree() *bptree.BPTree {
	return bptree.NewBPTreeN(16)
}