mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
385 lines
13 KiB
Go
385 lines
13 KiB
Go
// Copyright (C) 2022, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package elasticsubnet
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/prompts"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/node/v2/v2/vms/platformvm/reward"
|
|
)
|
|
|
|
// default elastic config parameter values are from
|
|
// https://docs.lux.network/subnets/reference-elastic-subnets-parameters#primary-network-parameters-on-mainnet
|
|
const (
|
|
defaultInitialSupply = 240_000_000
|
|
defaultMaximumSupply = 720_000_000
|
|
defaultMinConsumptionRate = 0.1
|
|
defaultMaxConsumptionRate = 0.12
|
|
defaultMinValidatorStake = 2_000
|
|
defaultMaxValidatorStake = 3_000_000
|
|
defaultMinStakeDurationHours = 14 * 24
|
|
defaultMinStakeDurationHoursString = "14 x 24"
|
|
defaultMinStakeDuration = defaultMinStakeDurationHours * time.Hour
|
|
defaultMaxStakeDurationHours = 365 * 24
|
|
defaultMaxStakeDurationHoursString = "365 x 24"
|
|
defaultMaxStakeDuration = defaultMaxStakeDurationHours * time.Hour
|
|
defaultMinDelegationFee = 20_000
|
|
defaultMinDelegatorStake = 25
|
|
defaultMaxValidatorWeightFactor = 5
|
|
defaultUptimeRequirement = 0.8
|
|
)
|
|
|
|
func GetElasticSubnetConfig(app *application.Lux, tokenSymbol string, useDefaultConfig bool) (models.ElasticSubnetConfig, error) {
|
|
const (
|
|
defaultConfig = "Use default elastic subnet config"
|
|
customizeConfig = "Customize elastic subnet config"
|
|
)
|
|
elasticSubnetConfig := models.ElasticSubnetConfig{
|
|
InitialSupply: defaultInitialSupply,
|
|
MaxSupply: defaultMaximumSupply,
|
|
MinConsumptionRate: defaultMinConsumptionRate * reward.PercentDenominator,
|
|
MaxConsumptionRate: defaultMaxConsumptionRate * reward.PercentDenominator,
|
|
MinValidatorStake: defaultMinValidatorStake,
|
|
MaxValidatorStake: defaultMaxValidatorStake,
|
|
MinStakeDuration: defaultMinStakeDuration,
|
|
MaxStakeDuration: defaultMaxStakeDuration,
|
|
MinDelegationFee: defaultMinDelegationFee,
|
|
MinDelegatorStake: defaultMinDelegatorStake,
|
|
MaxValidatorWeightFactor: defaultMaxValidatorWeightFactor,
|
|
UptimeRequirement: defaultUptimeRequirement * reward.PercentDenominator,
|
|
}
|
|
if useDefaultConfig {
|
|
return elasticSubnetConfig, nil
|
|
}
|
|
elasticSubnetConfigOptions := []string{defaultConfig, customizeConfig}
|
|
chosenConfig, err := app.Prompt.CaptureList(
|
|
"How would you like to set fees",
|
|
elasticSubnetConfigOptions,
|
|
)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
|
|
if chosenConfig == defaultConfig {
|
|
return elasticSubnetConfig, nil
|
|
}
|
|
customElasticSubnetConfig, err := getCustomElasticSubnetConfig(app, tokenSymbol)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
return customElasticSubnetConfig, nil
|
|
}
|
|
|
|
func getCustomElasticSubnetConfig(app *application.Lux, tokenSymbol string) (models.ElasticSubnetConfig, error) {
|
|
ux.Logger.PrintToUser("More info regarding elastic subnet parameters can be found at https://docs.lux.network/subnets/reference-elastic-subnets-parameters")
|
|
initialSupply, err := getInitialSupply(app, tokenSymbol)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
maxSupply, err := getMaximumSupply(app, tokenSymbol, initialSupply)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
minConsumptionRate, maxConsumptionRate, err := getConsumptionRate(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
minValidatorStake, maxValidatorStake, err := getValidatorStake(app, initialSupply, maxSupply)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
minStakeDuration, maxStakeDuration, err := getStakeDuration(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
minDelegationFee, err := getMinDelegationFee(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
minDelegatorStake, err := getMinDelegatorStake(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
maxValidatorWeightFactor, err := getMaxValidatorWeightFactor(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
uptimeReq, err := getUptimeRequirement(app)
|
|
if err != nil {
|
|
return models.ElasticSubnetConfig{}, err
|
|
}
|
|
|
|
elasticSubnetConfig := models.ElasticSubnetConfig{
|
|
InitialSupply: initialSupply,
|
|
MaxSupply: maxSupply,
|
|
MinConsumptionRate: minConsumptionRate,
|
|
MaxConsumptionRate: maxConsumptionRate,
|
|
MinValidatorStake: minValidatorStake,
|
|
MaxValidatorStake: maxValidatorStake,
|
|
MinStakeDuration: minStakeDuration,
|
|
MaxStakeDuration: maxStakeDuration,
|
|
MinDelegationFee: minDelegationFee,
|
|
MinDelegatorStake: minDelegatorStake,
|
|
MaxValidatorWeightFactor: maxValidatorWeightFactor,
|
|
UptimeRequirement: uptimeReq,
|
|
}
|
|
return elasticSubnetConfig, err
|
|
}
|
|
|
|
func getInitialSupply(app *application.Lux, tokenName string) (uint64, error) {
|
|
ux.Logger.PrintToUser("Select the Initial Supply of %s. \"_\" can be used as thousand separator", tokenName)
|
|
ux.Logger.PrintToUser("Mainnet Initial Supply is %s", ux.ConvertToStringWithThousandSeparator(defaultInitialSupply))
|
|
initialSupply, err := app.Prompt.CaptureUint64("Initial Supply amount")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return initialSupply, nil
|
|
}
|
|
|
|
func getMaximumSupply(app *application.Lux, tokenName string, initialSupply uint64) (uint64, error) {
|
|
ux.Logger.PrintToUser("Select the Maximum Supply of %s. \"_\" can be used as thousand separator", tokenName)
|
|
ux.Logger.PrintToUser("Mainnet Maximum Supply is %s", ux.ConvertToStringWithThousandSeparator(defaultMaximumSupply))
|
|
maxSupply, err := app.Prompt.CaptureUint64Compare(
|
|
"Maximum Supply amount",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Initial Supply",
|
|
Type: prompts.MoreThanEq,
|
|
Value: initialSupply,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return maxSupply, nil
|
|
}
|
|
|
|
func getConsumptionRate(app *application.Lux) (uint64, uint64, error) {
|
|
ux.Logger.PrintToUser("Select the Minimum Consumption Rate. Please denominate your percentage in PercentDenominator")
|
|
ux.Logger.PrintToUser("To denominate your percentage in PercentDenominator just multiply it by 10_000. For example, 1 percent corresponds to 10_000")
|
|
ux.Logger.PrintToUser("Mainnet Minimum Consumption Rate is %s", ux.ConvertToStringWithThousandSeparator(uint64(defaultMinConsumptionRate*reward.PercentDenominator)))
|
|
minConsumptionRate, err := app.Prompt.CaptureUint64Compare(
|
|
"Minimum Consumption Rate",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Percent Denominator(1_0000_0000)",
|
|
Type: prompts.LessThanEq,
|
|
Value: reward.PercentDenominator,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Select the Maximum Consumption Rate. Please denominate your percentage in PercentDenominator")
|
|
ux.Logger.PrintToUser("To denominate your percentage in PercentDenominator just multiply it by 10_000. For example, 1 percent corresponds to 10_000")
|
|
ux.Logger.PrintToUser("Mainnet Maximum Consumption Rate is %s", ux.ConvertToStringWithThousandSeparator(uint64(defaultMaxConsumptionRate*reward.PercentDenominator)))
|
|
maxConsumptionRate, err := app.Prompt.CaptureUint64Compare(
|
|
"Maximum Consumption Rate",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Percent Denominator(1_0000_0000)",
|
|
Type: prompts.LessThanEq,
|
|
Value: reward.PercentDenominator,
|
|
},
|
|
{
|
|
Label: "Minimum Consumption Rate",
|
|
Type: prompts.MoreThanEq,
|
|
Value: minConsumptionRate,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return minConsumptionRate, maxConsumptionRate, nil
|
|
}
|
|
|
|
func getValidatorStake(app *application.Lux, initialSupply uint64, maximumSupply uint64) (uint64, uint64, error) {
|
|
ux.Logger.PrintToUser("Select the Minimum Validator Stake. \"_\" can be used as thousand separator")
|
|
ux.Logger.PrintToUser("Mainnet Minimum Validator Stake is %s", ux.ConvertToStringWithThousandSeparator(defaultMinValidatorStake))
|
|
minValidatorStake, err := app.Prompt.CaptureUint64Compare(
|
|
"Minimum Validator Stake",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Initial Supply",
|
|
Type: prompts.LessThanEq,
|
|
Value: initialSupply,
|
|
},
|
|
{
|
|
Label: "0",
|
|
Type: prompts.MoreThan,
|
|
Value: 0,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Select the Maximum Validator Stake. \"_\" can be used as thousand separator")
|
|
ux.Logger.PrintToUser("Mainnet Maximum Validator Stake is %s", ux.ConvertToStringWithThousandSeparator(defaultMaxValidatorStake))
|
|
maxValidatorStake, err := app.Prompt.CaptureUint64Compare(
|
|
"Maximum Validator Stake",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Maximum Supply",
|
|
Type: prompts.LessThanEq,
|
|
Value: maximumSupply,
|
|
},
|
|
{
|
|
Label: "Minimum Validator Stake",
|
|
Type: prompts.MoreThan,
|
|
Value: minValidatorStake,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return minValidatorStake, maxValidatorStake, nil
|
|
}
|
|
|
|
func getStakeDuration(app *application.Lux) (time.Duration, time.Duration, error) {
|
|
ux.Logger.PrintToUser("Select the Minimum Stake Duration. Please enter in units of hours")
|
|
ux.Logger.PrintToUser("Mainnet Minimum Stake Duration is %d (%s)", defaultMinStakeDurationHours, defaultMinStakeDurationHoursString)
|
|
minStakeDuration, err := app.Prompt.CaptureUint64Compare(
|
|
"Minimum Stake Duration",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "0",
|
|
Type: prompts.MoreThan,
|
|
Value: 0,
|
|
},
|
|
{
|
|
Label: "Global Max Stake Duration",
|
|
Type: prompts.LessThanEq,
|
|
Value: uint64(defaultMaxStakeDurationHours),
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Select the Maximum Stake Duration")
|
|
ux.Logger.PrintToUser("Mainnet Maximum Stake Duration is %d (%s)", defaultMaxStakeDurationHours, defaultMaxStakeDurationHoursString)
|
|
maxStakeDuration, err := app.Prompt.CaptureUint64Compare(
|
|
"Maximum Stake Duration",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Minimum Stake Duration",
|
|
Type: prompts.MoreThanEq,
|
|
Value: minStakeDuration,
|
|
},
|
|
{
|
|
Label: "Global Max Stake Duration",
|
|
Type: prompts.LessThanEq,
|
|
Value: uint64(defaultMaxStakeDurationHours),
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
return time.Duration(minStakeDuration) * time.Hour, time.Duration(maxStakeDuration) * time.Hour, nil
|
|
}
|
|
|
|
func getMinDelegationFee(app *application.Lux) (uint32, error) {
|
|
ux.Logger.PrintToUser("Select the Minimum Delegation Fee. Please denominate your percentage in PercentDenominator")
|
|
ux.Logger.PrintToUser("To denominate your percentage in PercentDenominator just multiply it by 10_000. For example, 1 percent corresponds to 10_000")
|
|
ux.Logger.PrintToUser("Mainnet Minimum Delegation Fee is %s", ux.ConvertToStringWithThousandSeparator(uint64(defaultMinDelegationFee)))
|
|
minDelegationFee, err := app.Prompt.CaptureUint64Compare(
|
|
"Minimum Delegation Fee",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Percent Denominator(1_0000_0000)",
|
|
Type: prompts.LessThanEq,
|
|
Value: reward.PercentDenominator,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if minDelegationFee > math.MaxInt32 {
|
|
return 0, fmt.Errorf("minimum Delegation Fee needs to be unsigned 32-bit integer")
|
|
}
|
|
return uint32(minDelegationFee), nil
|
|
}
|
|
|
|
func getMinDelegatorStake(app *application.Lux) (uint64, error) {
|
|
ux.Logger.PrintToUser("Select the Minimum Delegator Stake")
|
|
ux.Logger.PrintToUser("Mainnet Minimum Delegator Stake is %d", defaultMinDelegatorStake)
|
|
minDelegatorStake, err := app.Prompt.CaptureUint64Compare(
|
|
"Minimum Delegator Stake",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "0",
|
|
Type: prompts.MoreThan,
|
|
Value: 0,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return minDelegatorStake, nil
|
|
}
|
|
|
|
func getMaxValidatorWeightFactor(app *application.Lux) (byte, error) {
|
|
ux.Logger.PrintToUser("Select the Maximum Validator Weight Factor. A value of 1 effectively disables delegation")
|
|
ux.Logger.PrintToUser("More info can be found at https://docs.lux.network/subnets/reference-elastic-subnets-parameters#delegators-weight-checks")
|
|
ux.Logger.PrintToUser("Mainnet Maximum Validator Weight Factor is %d", defaultMaxValidatorWeightFactor)
|
|
maxValidatorWeightFactor, err := app.Prompt.CaptureUint64Compare(
|
|
"Maximum Validator Weight Factor",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "0",
|
|
Type: prompts.MoreThan,
|
|
Value: 0,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if maxValidatorWeightFactor > math.MaxInt8 {
|
|
return 0, fmt.Errorf("maximum Validator Weight Factor needs to be unsigned 8-bit integer")
|
|
}
|
|
return byte(maxValidatorWeightFactor), nil
|
|
}
|
|
|
|
func getUptimeRequirement(app *application.Lux) (uint32, error) {
|
|
ux.Logger.PrintToUser("Select the Uptime Requirement. Please denominate your percentage in PercentDenominator")
|
|
ux.Logger.PrintToUser("To denominate your percentage in PercentDenominator just multiply it by 10_000. For example, 1 percent corresponds to 10_000")
|
|
ux.Logger.PrintToUser("Mainnet Uptime Requirement is %s", ux.ConvertToStringWithThousandSeparator(uint64(defaultUptimeRequirement*reward.PercentDenominator)))
|
|
uptimeReq, err := app.Prompt.CaptureUint64Compare(
|
|
"Uptime Requirement",
|
|
[]prompts.Comparator{
|
|
{
|
|
Label: "Percent Denominator(1_0000_0000)",
|
|
Type: prompts.LessThanEq,
|
|
Value: reward.PercentDenominator,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if uptimeReq > math.MaxInt32 {
|
|
return 0, fmt.Errorf("uptime Requirement needs to be unsigned 32-bit integer")
|
|
}
|
|
return uint32(uptimeReq), nil
|
|
}
|