Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

298 add missing commands for the vested staking #83

Merged
merged 9 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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:
Expand Down Expand Up @@ -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:

Expand All @@ -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 |
Expand Down
6 changes: 3 additions & 3 deletions command/sidechain/commission/commission.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
93 changes: 91 additions & 2 deletions command/sidechain/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions command/sidechain/registration/register_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down Expand Up @@ -235,7 +235,7 @@ func stake(sender txrelayer.TxRelayer, account *wallet.Account) (*ethgo.Receipt,

txn := &ethgo.Transaction{
Input: encoded,
To: (*ethgo.Address)(&stakeManager),
To: (*ethgo.Address)(&hydraStaking),
Value: stake,
}

Expand Down
52 changes: 40 additions & 12 deletions command/sidechain/staking/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

var (
delegateAddressFlag = "delegate"
vestingPeriodFlag = "vesting-period"
)

type stakeParams struct {
Expand All @@ -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")

Expand Down
Loading