Skip to content

Commit

Permalink
Add "validator yield".
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdee committed Jun 12, 2022
1 parent d919810 commit e144217
Show file tree
Hide file tree
Showing 13 changed files with 618 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
1.24.0:
- add "validator yield"

1.23.1:
- do not fetch future state for chain eth1votes

Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func includeCommandBindings(cmd *cobra.Command) {
validatorInfoBindings()
case "validator/keycheck":
validatorKeycheckBindings()
case "validator/yield":
validatorYieldBindings()
case "validator/expectation":
validatorExpectationBindings()
case "wallet/create":
Expand Down
84 changes: 84 additions & 0 deletions cmd/validator/yield/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright © 2022 Weald Technology Trading.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validatoryield

import (
"context"
"time"

eth2client "github.com/attestantio/go-eth2-client"
"github.com/pkg/errors"
"github.com/shopspring/decimal"
"github.com/spf13/viper"
)

type command struct {
quiet bool
verbose bool
debug bool
json bool

// Beacon node connection.
timeout time.Duration
connection string
allowInsecureConnections bool

// Input.
validators string

// Data access.
eth2Client eth2client.Service

// Output.
results *output
}

type output struct {
BaseReward decimal.Decimal `json:"base_reward"`
ActiveValidators decimal.Decimal `json:"active_validators"`
ActiveValidatorBalance decimal.Decimal `json:"active_validator_balance"`
ValidatorRewardsPerEpoch decimal.Decimal `json:"validator_rewards_per_epoch"`
ValidatorRewardsPerYear decimal.Decimal `json:"validator_rewards_per_year"`
ValidatorRewardsAllCorrect decimal.Decimal `json:"validator_rewards_all_correct"`
ExpectedValidatorRewardsPerEpoch decimal.Decimal `json:"expected_validator_rewards_per_epoch"`
MaxIssuancePerEpoch decimal.Decimal `json:"max_issuance_per_epoch"`
MaxIssuancePerYear decimal.Decimal `json:"max_issuance_per_year"`
Yield decimal.Decimal `json:"yield"`
}

func newCommand(ctx context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
json: viper.GetBool("json"),
results: &output{},
}

// Timeout.
if viper.GetDuration("timeout") == 0 {
return nil, errors.New("timeout is required")
}
c.timeout = viper.GetDuration("timeout")

if viper.GetString("connection") == "" {
return nil, errors.New("connection is required")
}
c.connection = viper.GetString("connection")
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")

c.validators = viper.GetString("validators")

return c, nil
}
73 changes: 73 additions & 0 deletions cmd/validator/yield/command_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright © 2021 Weald Technology Trading.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validatoryield

import (
"context"
"os"
"testing"

"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)

func TestInput(t *testing.T) {
if os.Getenv("ETHDO_TEST_CONNECTION") == "" {
t.Skip("ETHDO_TEST_CONNECTION not configured; cannot run tests")
}

tests := []struct {
name string
vars map[string]interface{}
err string
}{
{
name: "TimeoutMissing",
vars: map[string]interface{}{},
err: "timeout is required",
},
{
name: "ConnectionMissing",
vars: map[string]interface{}{
"validators": "1",
"timeout": "5s",
},
err: "connection is required",
},
{
name: "Good",
vars: map[string]interface{}{
"validators": "1",
"timeout": "5s",
"connection": os.Getenv("ETHDO_TEST_CONNECTION"),
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
viper.Reset()

for k, v := range test.vars {
viper.Set(k, v)
}
_, err := newCommand(context.Background())
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
}
})
}
}
67 changes: 67 additions & 0 deletions cmd/validator/yield/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright © 2021 Weald Technology Trading.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validatoryield

import (
"context"
"encoding/json"
"strings"

"github.com/shopspring/decimal"
"github.com/wealdtech/go-string2eth"
)

func (c *command) output(ctx context.Context) (string, error) {
if c.quiet {
return "", nil
}

if c.json {
data, err := json.Marshal(c.results)
if err != nil {
return "", err
}
return string(data), nil
}

builder := strings.Builder{}

if c.verbose {
builder.WriteString("Per-validator rewards per epoch: ")
builder.WriteString(string2eth.WeiToGWeiString(c.results.ValidatorRewardsPerEpoch.BigInt()))
builder.WriteString("\n")

builder.WriteString("Per-validator rewards per year: ")
builder.WriteString(string2eth.WeiToString(c.results.ValidatorRewardsPerYear.BigInt(), true))
builder.WriteString("\n")

builder.WriteString("Expected per-validator rewards per epoch (with full participation): ")
builder.WriteString(string2eth.WeiToGWeiString(c.results.ExpectedValidatorRewardsPerEpoch.BigInt()))
builder.WriteString("\n")

builder.WriteString("Maximum chain issuance per epoch: ")
builder.WriteString(string2eth.WeiToString(c.results.MaxIssuancePerEpoch.BigInt(), true))
builder.WriteString("\n")

builder.WriteString("Maximum chain issuance per year: ")
builder.WriteString(string2eth.WeiToString(c.results.MaxIssuancePerYear.BigInt(), true))
builder.WriteString("\n")
}

builder.WriteString("Yield: ")
builder.WriteString(c.results.Yield.Mul(decimal.New(100, 0)).StringFixed(2))
builder.WriteString("%\n")

return builder.String(), nil
}
Loading

0 comments on commit e144217

Please sign in to comment.