From 02f0619faea8a979c8a4668c41913e0d1044e80a Mon Sep 17 00:00:00 2001 From: Vitomir Pavlov Date: Mon, 2 Dec 2024 18:11:19 +0200 Subject: [PATCH] 298 add missing commands for the vested staking (#83) * add the stake vesting functionality update the bindings to include the stakeWithVesting and getValidator; add new flags - vesting and vesting-period, and verify the period to e in the valid period; create a new createStakeTransaction function in order to make the code more readable and cleaner; optimize the code in the logs loop; make the amount flag mandatory; mark flags mutually exclusive with the delegate flag; make a MaxVestingPeriod constant in the sidechain helper; fix the conditions in the commission command event handling; rename stakeManager -> hydraStaking in the register_validator to avoid confusion; create ValidatorStatus enum and GetStatus function in the validator package; create a new getValidatorInfo helper function to get the data that I need; update docs; --------- Co-authored-by: Rosen Santev <77731162+R-Santev@users.noreply.github.com> --- README.md | 26 +++- command/sidechain/commission/commission.go | 6 +- command/sidechain/helper.go | 93 +++++++++++- .../registration/register_validator.go | 4 +- command/sidechain/staking/params.go | 52 +++++-- command/sidechain/staking/stake.go | 136 ++++++++++++------ .../polybft/contractsapi/bindings-gen/main.go | 2 + .../polybft/contractsapi/contractsapi.go | 32 +++++ h_docs/polybft_setup.md | 26 +++- 9 files changed, 301 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index fb874b65..aaa77cd8 100644 --- a/README.md +++ b/README.md @@ -166,14 +166,12 @@ Hydra's validator set is unique as it offers a permissionless opportunity on a f After ensuring you have a minimum of 15,000 HYDRA in your validator wallet, you can execute the following command. ``` -hydra hydragon register-validator --data-dir ./node-secrets --stake 15000000000000000000000 --jsonrpc http://localhost:8545 +hydra hydragon register-validator --data-dir ./node-secrets --stake 15000000000000000000000 --commission 10 --jsonrpc http://localhost:8545 ``` -The command above both registers the validator and stakes the specified amount. To specify an initial commission rate that will be deducted from future delegators’ rewards, use the `--commission` flag with the desired value. The commission rate must be between 0 and 100. The command will look as follows: +The command above both registers the validator, stakes the specified amount and sets the commission (in percentage). This commission will be deducted from future delegators’ rewards. Change the value next to the `--commission` flag with the desired commission and ensure it is between 0 and 100. -``` -hydra hydragon register-validator --data-dir ./node-secrets --stake 15000000000000000000000 --commission 10 --jsonrpc http://localhost:8545 -``` +#### Stake Use the following command if you want to perform a stake operation only or increase your existing stake: @@ -185,6 +183,20 @@ hydra hydragon stake --data-dir ./node-secrets --self true --amount 150000000000 Congratulations! You have successfully become a validator on the Hydra Chain. For further information and support, join our Telegram group and engage with the community. +#### Stake with Vesting + +Hydra Chain allows you to open a vested position, where your funds are locked for a chosen period between 1 and 52 weeks (1 year). In return, you receive a loyalty bonus on the APR. The longer the vesting duration, the higher the bonus. + +You can unstake your funds prematurely by paying a penalty fee, which is calculated at 0.5% per remaining week of the lockup period. Consequently, the closer the position is to maturity, the lower the penalty fee, while the further away, the higher the cost. Additionally, any rewards distributed to you in the vesting period will also be burned in the process. For more details on this mechanism, refer to the Whitepaper. + +``` +hydra hydragon stake --data-dir ./node-secrets --self true --amount 10000000000000000000000 --vesting-period 52 --jsonrpc http://localhost:8545 +``` + +**Note:** The amounts are specified in wei, and the specified value will be added to your existing staked amount, if applicable. + +Congratulations! Enjoy the enhanced rewards and benefits provided by Vested Staking. + ### Update the commission for the delegators After becoming a validator, you can still update the commission rate if needed. The process begins with executing a command to set the new commission as pending. This is followed by a 15-day waiting period, after which you can apply the updated commission. This waiting period is designed to prevent validators from acting dishonestly by executing commission changes shortly before delegators claim their rewards. You can initialize the new commission using the following command: @@ -242,7 +254,7 @@ To reduce the risk of stalling caused by validators experiencing temporary issue ### Command Line Interface -Here are the HydraChain node CLI commands that currently can be used: +Here are the Hydra Chain node CLI commands that currently can be used: - Usage: @@ -259,7 +271,7 @@ Here are the HydraChain node CLI commands that currently can be used: | completion | Generate the autocompletion script for the specified shell | | genesis | Generates the genesis configuration file with the passed in parameters | | help | Help about any command | -| hydragon | Executes HydraChain's Hydragon consensus commands, including staking, unstaking, rewards management, and validator operations | +| hydragon | Executes Hydra Chain's Hydragon consensus commands, including staking, unstaking, rewards management, and validator operations | | license | Returns Hydra Chain license and dependency attributions | | monitor | Starts logging block add / remove events on the blockchain | | peers | Top level command for interacting with the network peers. Only accepts subcommands | diff --git a/command/sidechain/commission/commission.go b/command/sidechain/commission/commission.go index dea45edd..c6bdad60 100644 --- a/command/sidechain/commission/commission.go +++ b/command/sidechain/commission/commission.go @@ -187,12 +187,12 @@ func runCommand(cmd *cobra.Command, _ []string) error { } } - if params.apply && !foundCommissionUpdatedLog { //nolint:gocritic + if !params.apply && !params.claim && !foundPendingCommissionLog { //nolint:gocritic + return fmt.Errorf("could not find an appropriate log in the receipt that validates the new pending commission") + } else if params.apply && !foundCommissionUpdatedLog { return fmt.Errorf("could not find an appropriate log in the receipt that validates the new commission update") } else if params.claim && !foundClaimCommissionLog { return fmt.Errorf("could not find an appropriate log in the receipt that validates the commission claim") - } else if !foundPendingCommissionLog { - return fmt.Errorf("could not find an appropriate log in the receipt that validates the new pending commission") } outputter.WriteCommandResult(result) diff --git a/command/sidechain/helper.go b/command/sidechain/helper.go index 5245373e..2c9af05c 100644 --- a/command/sidechain/helper.go +++ b/command/sidechain/helper.go @@ -7,7 +7,13 @@ import ( "os" "github.com/0xPolygon/polygon-edge/command/polybftsecrets" + "github.com/0xPolygon/polygon-edge/consensus/polybft" + "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" + "github.com/0xPolygon/polygon-edge/contracts" + "github.com/0xPolygon/polygon-edge/helper/hex" + "github.com/0xPolygon/polygon-edge/txrelayer" + "github.com/0xPolygon/polygon-edge/types" "github.com/umbracle/ethgo" ) @@ -16,8 +22,9 @@ const ( AmountFlag = "amount" InsecureLocalStoreFlag = "insecure" - DefaultGasPrice = 1879048192 // 0x70000000 - MaxCommission = 100 + DefaultGasPrice = 1879048192 // 0x70000000 + MaxCommission = 100 + MaxVestingPeriod = 52 ) func CheckIfDirectoryExist(dir string) error { @@ -74,3 +81,85 @@ func CreateTransaction(sender ethgo.Address, to *ethgo.Address, input []byte, va return txn } + +// Define the ValidatorStatus enum +type ValidatorStatus int + +const ( + None ValidatorStatus = iota + Registered + Active + Banned +) + +// Function to get ValidatorStatus based on number value +func GetStatus(value int) ValidatorStatus { + switch value { + case 1: + return Active + case 2: + return Active + case 3: + return Banned + default: + return None + } +} + +// GetValidatorInfo queries HydraChain smart contract to retrieve the validator info for given address +func GetValidatorInfo(txRelayer txrelayer.TxRelayer, validatorAddr ethgo.Address) (*polybft.ValidatorInfo, error) { + var getValidatorFn = &contractsapi.GetValidatorHydraChainFn{ + ValidatorAddress: types.Address(validatorAddr), + } + + encoded, err := getValidatorFn.EncodeAbi() + if err != nil { + return nil, err + } + + response, err := txRelayer.Call(validatorAddr, (ethgo.Address)(contracts.HydraChainContract), encoded) + if err != nil { + return nil, err + } + + byteResponse, err := hex.DecodeHex(response) + if err != nil { + return nil, fmt.Errorf("unable to decode hex response, %w", err) + } + + getValidatorMethod := contractsapi.HydraChain.Abi.GetMethod("getValidator") + + decoded, err := getValidatorMethod.Outputs.Decode(byteResponse) + if err != nil { + return nil, err + } + + decodedOutputsMap, ok := decoded.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("could not convert decoded outputs to map") + } + + stake, ok := decodedOutputsMap["stake"].(*big.Int) + if !ok { + return nil, fmt.Errorf("could not convert stake to big.Int") + } + + withdrawableRewards, ok := decodedOutputsMap["withdrawableRewards"].(*big.Int) + if !ok { + return nil, fmt.Errorf("could not convert withdrawableRewards to big.Int") + } + + status, ok := decodedOutputsMap["status"].(uint8) + if !ok { + return nil, fmt.Errorf("could not convert status to uint8") + } + + validatorInfo := &polybft.ValidatorInfo{ + Address: validatorAddr, + Stake: stake, + WithdrawableRewards: withdrawableRewards, + IsActive: Active == GetStatus(int(status)), + } + + return validatorInfo, nil +} diff --git a/command/sidechain/registration/register_validator.go b/command/sidechain/registration/register_validator.go index 999386dd..b795b066 100644 --- a/command/sidechain/registration/register_validator.go +++ b/command/sidechain/registration/register_validator.go @@ -28,7 +28,7 @@ var ( params registerParams hydraChain = contracts.HydraChainContract - stakeManager = contracts.HydraStakingContract + hydraStaking = contracts.HydraStakingContract newValidatorEventABI = contractsapi.HydraChain.Abi.Events["NewValidator"] commissionUpdatedEventABI = contractsapi.HydraDelegation.Abi.Events["CommissionUpdated"] stakeEventABI = contractsapi.HydraStaking.Abi.Events["Staked"] @@ -235,7 +235,7 @@ func stake(sender txrelayer.TxRelayer, account *wallet.Account) (*ethgo.Receipt, txn := ðgo.Transaction{ Input: encoded, - To: (*ethgo.Address)(&stakeManager), + To: (*ethgo.Address)(&hydraStaking), Value: stake, } diff --git a/command/sidechain/staking/params.go b/command/sidechain/staking/params.go index 1cefe745..c87c486e 100644 --- a/command/sidechain/staking/params.go +++ b/command/sidechain/staking/params.go @@ -10,6 +10,7 @@ import ( var ( delegateAddressFlag = "delegate" + vestingPeriodFlag = "vesting-period" ) type stakeParams struct { @@ -18,45 +19,72 @@ type stakeParams struct { jsonRPC string amount string self bool + vestingPeriod uint64 delegateAddress string insecureLocalStore bool } -func (v *stakeParams) validateFlags() error { - if _, err := helper.ParseJSONRPCAddress(v.jsonRPC); err != nil { +func (sp *stakeParams) getRequiredFlags() []string { + return []string{ + sidechainHelper.AmountFlag, + } +} + +func (sp *stakeParams) validateFlags() error { + if _, err := helper.ParseJSONRPCAddress(sp.jsonRPC); err != nil { return fmt.Errorf("failed to parse json rpc address. Error: %w", err) } - return sidechainHelper.ValidateSecretFlags(v.accountDir, v.accountConfig) + if sp.vestingPeriod != 0 && (sp.vestingPeriod < 1 || sp.vestingPeriod > sidechainHelper.MaxVestingPeriod) { + return fmt.Errorf( + "invalid vesting period '%d'. The period must between 1 and '%d' weeks", + sp.vestingPeriod, + sidechainHelper.MaxVestingPeriod, + ) + } + + return sidechainHelper.ValidateSecretFlags(sp.accountDir, sp.accountConfig) } type stakeResult struct { validatorAddress string isSelfStake bool amount string + vestingPeriod uint64 delegatedTo string } func (sr stakeResult) GetOutput() string { var buffer bytes.Buffer + var title string + var vals []string if sr.isSelfStake { - buffer.WriteString("\n[SELF STAKE]\n") + title = "\n[SELF STAKE]\n" + + vals = []string{ + fmt.Sprintf("Validator Address|%s", sr.validatorAddress), + fmt.Sprintf("Amount Staked|%v", sr.amount), + } + + if sr.vestingPeriod > 0 { + title = "\n[VESTED STAKING ACTIVATED]\n" - vals = make([]string, 0, 2) - vals = append(vals, fmt.Sprintf("Validator Address|%s", sr.validatorAddress)) - vals = append(vals, fmt.Sprintf("Amount Staked|%v", sr.amount)) + vals = append(vals, fmt.Sprintf("Vesting Period (in weeks)|%d", sr.vestingPeriod)) + } } else { - buffer.WriteString("\n[DELEGATED AMOUNT]\n") + title = "\n[DELEGATED AMOUNT]\n" - vals = make([]string, 0, 3) - vals = append(vals, fmt.Sprintf("Validator Address|%s", sr.validatorAddress)) - vals = append(vals, fmt.Sprintf("Amount Delegated|%v", sr.amount)) - vals = append(vals, fmt.Sprintf("Delegated To|%s", sr.delegatedTo)) + vals = []string{ + fmt.Sprintf("Validator Address|%s", sr.validatorAddress), + fmt.Sprintf("Amount Delegated|%v", sr.amount), + fmt.Sprintf("Delegated To|%s", sr.delegatedTo), + } } + buffer.WriteString(title) buffer.WriteString(helper.FormatKV(vals)) buffer.WriteString("\n") diff --git a/command/sidechain/staking/stake.go b/command/sidechain/staking/stake.go index bd54f611..6379438b 100644 --- a/command/sidechain/staking/stake.go +++ b/command/sidechain/staking/stake.go @@ -10,6 +10,7 @@ import ( "github.com/0xPolygon/polygon-edge/command/polybftsecrets" "github.com/0xPolygon/polygon-edge/command/sidechain" "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" + "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" "github.com/0xPolygon/polygon-edge/contracts" "github.com/0xPolygon/polygon-edge/helper/common" "github.com/0xPolygon/polygon-edge/txrelayer" @@ -21,8 +22,6 @@ import ( var ( params stakeParams - stakeFn = contractsapi.HydraStaking.Abi.Methods["stake"] - delegateFn = contractsapi.HydraDelegation.Abi.Methods["delegate"] stakeEventABI = contractsapi.HydraStaking.Abi.Events["Staked"] delegateEventABI = contractsapi.HydraDelegation.Abi.Events["Delegated"] ) @@ -35,9 +34,10 @@ func GetCommand() *cobra.Command { RunE: runCommand, } - helper.RegisterJSONRPCFlag(stakeCmd) setFlags(stakeCmd) + helper.SetRequiredFlags(stakeCmd, params.getRequiredFlags()) + return stakeCmd } @@ -56,6 +56,13 @@ func setFlags(cmd *cobra.Command) { polybftsecrets.AccountConfigFlagDesc, ) + cmd.Flags().StringVar( + ¶ms.amount, + sidechain.AmountFlag, + "", + "a mandatory flag which indicates the amount to self stake or delegate to another account", + ) + cmd.Flags().BoolVar( ¶ms.self, sidechain.SelfFlag, @@ -63,11 +70,12 @@ func setFlags(cmd *cobra.Command) { "indicates if its a self stake action", ) - cmd.Flags().StringVar( - ¶ms.amount, - sidechain.AmountFlag, - "0", - "amount to stake or delegate to another account", + cmd.Flags().Uint64Var( + ¶ms.vestingPeriod, + vestingPeriodFlag, + 0, + "this flag is used to open a vested staking position. It indicates the vesting period in weeks. "+ + "It must be at least 1 week and the max period is 52 weeks (1 year).", ) cmd.Flags().StringVar( @@ -84,7 +92,10 @@ func setFlags(cmd *cobra.Command) { "a flag to indicate if the secrets used are encrypted. If set to true, the secrets are stored in plain text.", ) + helper.RegisterJSONRPCFlag(cmd) + cmd.MarkFlagsMutuallyExclusive(sidechain.SelfFlag, delegateAddressFlag) + cmd.MarkFlagsMutuallyExclusive(vestingPeriodFlag, delegateAddressFlag) cmd.MarkFlagsMutuallyExclusive(polybftsecrets.AccountDirFlag, polybftsecrets.AccountConfigFlag) } @@ -107,44 +118,19 @@ func runCommand(cmd *cobra.Command, _ []string) error { return err } - txRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(params.jsonRPC), - txrelayer.WithReceiptTimeout(150*time.Millisecond)) + txRelayer, err := txrelayer.NewTxRelayer( + txrelayer.WithIPAddress(params.jsonRPC), + txrelayer.WithReceiptTimeout(150*time.Millisecond), + ) if err != nil { return err } - var encoded []byte - - var contractAddr *ethgo.Address - - if params.self { - encoded, err = stakeFn.Encode([]interface{}{}) - contractAddr = (*ethgo.Address)(&contracts.HydraStakingContract) - } else { - delegateToAddress := types.StringToAddress(params.delegateAddress) - - encoded, err = delegateFn.Encode([]interface{}{ - ethgo.Address(delegateToAddress), - }) - contractAddr = (*ethgo.Address)(&contracts.HydraDelegationContract) - } - + txn, err := createStakeTransaction(validatorAccount) if err != nil { return err } - parsedValue, err := common.ParseUint256orHex(¶ms.amount) - if err != nil { - return fmt.Errorf("cannot parse \"amount\" value %s", params.amount) - } - - txn := sidechain.CreateTransaction( - validatorAccount.Ecdsa.Address(), - contractAddr, - encoded, - parsedValue, - ) - receipt, err := txRelayer.SendTransaction(txn, validatorAccount.Ecdsa) if err != nil { return err @@ -156,14 +142,19 @@ func runCommand(cmd *cobra.Command, _ []string) error { result := &stakeResult{ validatorAddress: validatorAccount.Ecdsa.Address().String(), + vestingPeriod: params.vestingPeriod, } foundLog := false // check the logs to check for the result for _, log := range receipt.Logs { - if stakeEventABI.Match(log) { - event, err := stakeEventABI.ParseLog(log) + var event map[string]interface{} + + var match bool + + if match = stakeEventABI.Match(log); match { + event, err = stakeEventABI.ParseLog(log) if err != nil { return err } @@ -171,18 +162,25 @@ func runCommand(cmd *cobra.Command, _ []string) error { result.isSelfStake = true result.amount = event["amount"].(*big.Int).String() //nolint:forcetypeassert - foundLog = true - - break - } else if delegateEventABI.Match(log) { - event, err := delegateEventABI.ParseLog(log) + if params.vestingPeriod > 0 { + validatorInfo, err := sidechain.GetValidatorInfo(txRelayer, validatorAccount.Ecdsa.Address()) + if err != nil { + fmt.Printf("was unable to get the validator info %s", result.validatorAddress) + } else { + result.amount = validatorInfo.Stake.String() + } + } + } else if match = delegateEventABI.Match(log); match { + event, err = delegateEventABI.ParseLog(log) if err != nil { return err } result.amount = event["amount"].(*big.Int).String() //nolint:forcetypeassert result.delegatedTo = event["validator"].(ethgo.Address).String() //nolint:forcetypeassert + } + if match { foundLog = true break @@ -199,3 +197,51 @@ func runCommand(cmd *cobra.Command, _ []string) error { return nil } + +func createStakeTransaction(validatorAccount *wallet.Account) (*ethgo.Transaction, error) { + var ( + encoded []byte + contractAddr *ethgo.Address + err error + ) + + if params.self { + if params.vestingPeriod > 0 { + var stakeWithVestingFn = &contractsapi.StakeWithVestingHydraStakingFn{ + DurationWeeks: new(big.Int).SetUint64(params.vestingPeriod), + } + + encoded, err = stakeWithVestingFn.EncodeAbi() + } else { + var stakeFn = &contractsapi.StakeHydraStakingFn{} + encoded, err = stakeFn.EncodeAbi() + } + + contractAddr = (*ethgo.Address)(&contracts.HydraStakingContract) + } else { + var delegateFn = &contractsapi.DelegateHydraDelegationFn{ + Staker: types.StringToAddress(params.delegateAddress), + } + + encoded, err = delegateFn.EncodeAbi() + contractAddr = (*ethgo.Address)(&contracts.HydraDelegationContract) + } + + if err != nil { + return nil, err + } + + parsedValue, err := common.ParseUint256orHex(¶ms.amount) + if err != nil { + return nil, fmt.Errorf("cannot parse \"amount\" value %s", params.amount) + } + + txn := sidechain.CreateTransaction( + validatorAccount.Ecdsa.Address(), + contractAddr, + encoded, + parsedValue, + ) + + return txn, nil +} diff --git a/consensus/polybft/contractsapi/bindings-gen/main.go b/consensus/polybft/contractsapi/bindings-gen/main.go index eff7c0b6..9842618d 100644 --- a/consensus/polybft/contractsapi/bindings-gen/main.go +++ b/consensus/polybft/contractsapi/bindings-gen/main.go @@ -53,6 +53,7 @@ func main() { "distributeDAOIncentive", "syncValidatorsData", "terminateBanProcedure", + "getValidator", }, []string{ "NewValidator", @@ -69,6 +70,7 @@ func main() { []string{ "initialize", "stake", + "stakeWithVesting", "unstake", "distributeRewardsFor", "claimStakingRewards()", diff --git a/consensus/polybft/contractsapi/contractsapi.go b/consensus/polybft/contractsapi/contractsapi.go index f0a9a8cd..b2ebbff4 100644 --- a/consensus/polybft/contractsapi/contractsapi.go +++ b/consensus/polybft/contractsapi/contractsapi.go @@ -192,6 +192,22 @@ func (t *TerminateBanProcedureHydraChainFn) DecodeAbi(buf []byte) error { return decodeMethod(HydraChain.Abi.Methods["terminateBanProcedure"], buf, t) } +type GetValidatorHydraChainFn struct { + ValidatorAddress types.Address `abi:"validatorAddress"` +} + +func (g *GetValidatorHydraChainFn) Sig() []byte { + return HydraChain.Abi.Methods["getValidator"].ID() +} + +func (g *GetValidatorHydraChainFn) EncodeAbi() ([]byte, error) { + return HydraChain.Abi.Methods["getValidator"].Encode(g) +} + +func (g *GetValidatorHydraChainFn) DecodeAbi(buf []byte) error { + return decodeMethod(HydraChain.Abi.Methods["getValidator"], buf, g) +} + type NewValidatorEvent struct { Validator types.Address `abi:"validator"` BlsKey [4]*big.Int `abi:"blsKey"` @@ -366,6 +382,22 @@ func (s *StakeHydraStakingFn) DecodeAbi(buf []byte) error { return decodeMethod(HydraStaking.Abi.Methods["stake"], buf, s) } +type StakeWithVestingHydraStakingFn struct { + DurationWeeks *big.Int `abi:"durationWeeks"` +} + +func (s *StakeWithVestingHydraStakingFn) Sig() []byte { + return HydraStaking.Abi.Methods["stakeWithVesting"].ID() +} + +func (s *StakeWithVestingHydraStakingFn) EncodeAbi() ([]byte, error) { + return HydraStaking.Abi.Methods["stakeWithVesting"].Encode(s) +} + +func (s *StakeWithVestingHydraStakingFn) DecodeAbi(buf []byte) error { + return decodeMethod(HydraStaking.Abi.Methods["stakeWithVesting"], buf, s) +} + type UnstakeHydraStakingFn struct { Amount *big.Int `abi:"amount"` } diff --git a/h_docs/polybft_setup.md b/h_docs/polybft_setup.md index c928abb3..8419c9a3 100644 --- a/h_docs/polybft_setup.md +++ b/h_docs/polybft_setup.md @@ -91,7 +91,23 @@ Stake tx is made in this step as well ./hydra server --data-dir ./test-add-chain-1 --chain genesis.json --grpc-address :5006 --libp2p :30306 --jsonrpc :10006 --log-level DEBUG --log-to ./log-6 ``` -5. Update commission of the validator that will taken from the delegators' rewards. +5. Staking + +After registering, you can increase your stake at any time. Additionally, if you have previously unstaked, stopped your validator, and want to resume validating, you don’t need to register again. You can simply start validating again by adding a stake using the following command: + +``` +./hydra hydragon stake --data-dir ./test-add-chain-1 --self true --amount 10000000000000000000000 --jsonrpc http://127.0.0.1:10006 --insecure +``` + +To stake a vested position, use the same command but include the additional vesting flag: + +``` +./hydra hydragon stake --data-dir ./test-add-chain-1 --self true --amount 10000000000000000000000 --vesting-period 52 --jsonrpc http://127.0.0.1:10006 --insecure +``` + +**Note:** The specified amount will be added to your existing staked amount, if applicable. + +6. Update the commission of the validator that will taken from the delegators' rewards. Here is the command to use for initializing the new commission: @@ -105,7 +121,7 @@ Then, we have a 15-day waiting period before being able to apply the commission. ./hydra hydragon commission --data-dir ./test-add-chain-1 --apply true --jsonrpc http://127.0.0.1:10006 --insecure ``` -6. The new validator will join the consensus in the next epoch. Then, after each epoch, rewards will be generated and can be claimed with the following command: +7. The new validator will join the consensus in the next epoch. Then, after each epoch, rewards will be generated and can be claimed with the following command: ``` ./hydra hydragon claim-rewards --data-dir ./test-add-chain-1 --jsonrpc http://127.0.0.1:10006 --insecure @@ -117,13 +133,13 @@ Additionally, during rewards distribution, if a validator has delegators, an add ./hydra hydragon commission --data-dir ./test-add-chain-1 --claim true --jsonrpc http://127.0.0.1:10006 --insecure ``` -7. Re-activating the validator, if a ban initiation took place: +8. Re-activating the validator, if a ban initiation took place: ``` ./hydra hydragon terminate-ban --data-dir ./test-add-chain-1 --jsonrpc http://127.0.0.1:10001 --insecure ``` -8. If the validator was banned, then withdraw the funds left by executing: +9. If the validator was banned, then withdraw the funds left by executing: ``` ./hydra hydragon withdraw --banned --data-dir ./test-add-chain-1 --jsonrpc http://127.0.0.1:10001 --insecure @@ -235,7 +251,7 @@ After Hydra's team confirms you are whitelisted you have to register your accoun In the container's shell execute: ``` -./hydra hydragon register-validator --data-dir ./node --stake 15000000000000000000000 --jsonrpc http://localhost:8545 +./hydra hydragon register-validator --data-dir ./node --stake 15000000000000000000000 --commission 10 --jsonrpc http://localhost:8545 ``` The above command both register the validator and stakes the specified amount.