diff --git a/CHANGELOG.md b/CHANGELOG.md index 656d3a7..1a4479c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +1.24.0: + - add "validator yield" + 1.23.1: - do not fetch future state for chain eth1votes diff --git a/cmd/root.go b/cmd/root.go index 33b3d68..fed447e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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": diff --git a/cmd/validator/yield/command.go b/cmd/validator/yield/command.go new file mode 100644 index 0000000..5a0f30c --- /dev/null +++ b/cmd/validator/yield/command.go @@ -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 +} diff --git a/cmd/validator/yield/command_internal_test.go b/cmd/validator/yield/command_internal_test.go new file mode 100644 index 0000000..3a37e45 --- /dev/null +++ b/cmd/validator/yield/command_internal_test.go @@ -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) + } + }) + } +} diff --git a/cmd/validator/yield/output.go b/cmd/validator/yield/output.go new file mode 100644 index 0000000..87c13a5 --- /dev/null +++ b/cmd/validator/yield/output.go @@ -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 +} diff --git a/cmd/validator/yield/process.go b/cmd/validator/yield/process.go new file mode 100644 index 0000000..fb48df2 --- /dev/null +++ b/cmd/validator/yield/process.go @@ -0,0 +1,170 @@ +// 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" + "fmt" + "math/big" + "strconv" + + eth2client "github.com/attestantio/go-eth2-client" + "github.com/pkg/errors" + "github.com/shopspring/decimal" + standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard" + "github.com/wealdtech/ethdo/util" +) + +func (c *command) process(ctx context.Context) error { + // Obtain information we need to process. + if err := c.setup(ctx); err != nil { + return err + } + + if c.debug { + fmt.Printf("Active validators: %v\n", c.results.ActiveValidators) + fmt.Printf("Active validator balance: %v\n", c.results.ActiveValidatorBalance) + } + + return c.calculateYield(ctx) +} + +var weiPerGwei = decimal.New(1e9, 0) +var one = decimal.New(1, 0) +var epochsPerYear = decimal.New(225*365, 0) + +// calculateYield calculates yield from the number of active validators. +func (c *command) calculateYield(ctx context.Context) error { + + spec, err := c.eth2Client.(eth2client.SpecProvider).Spec(ctx) + if err != nil { + return err + } + + tmp, exists := spec["BASE_REWARD_FACTOR"] + if !exists { + return errors.New("spec missing BASE_REWARD_FACTOR") + } + baseReward, isType := tmp.(uint64) + if !isType { + return errors.New("BASE_REWARD_FACTOR of incorrect type") + } + if c.debug { + fmt.Printf("Base reward: %v\n", baseReward) + } + c.results.BaseReward = decimal.New(int64(baseReward), 0) + + numerator := decimal.New(32, 0).Mul(weiPerGwei).Mul(c.results.BaseReward) + if c.debug { + fmt.Printf("Numerator: %v\n", numerator) + } + activeValidatorsBalanceInGwei := c.results.ActiveValidatorBalance.Div(weiPerGwei) + denominator := decimal.NewFromBigInt(new(big.Int).Sqrt(activeValidatorsBalanceInGwei.BigInt()), 0) + if c.debug { + fmt.Printf("Denominator: %v\n", denominator) + } + c.results.ValidatorRewardsPerEpoch = numerator.Div(denominator).RoundDown(0).Mul(weiPerGwei) + if c.debug { + fmt.Printf("Validator rewards per epoch: %v\n", c.results.ValidatorRewardsPerEpoch) + } + c.results.ValidatorRewardsPerYear = c.results.ValidatorRewardsPerEpoch.Mul(epochsPerYear) + if c.debug { + fmt.Printf("Validator rewards per year: %v\n", c.results.ValidatorRewardsPerYear) + } + // Expected validator rewards assume that there is no proposal and no sync committee participation, + // but that head/source/target are correct and timely: this gives 54/64 of the reward. + // These values are obtained from https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#incentivization-weights + c.results.ExpectedValidatorRewardsPerEpoch = c.results.ValidatorRewardsPerEpoch.Mul(decimal.New(54, 0)).Div(decimal.New(64, 0)).Div(weiPerGwei).RoundDown(0).Mul(weiPerGwei) + if c.debug { + fmt.Printf("Expected validator rewards per epoch: %v\n", c.results.ExpectedValidatorRewardsPerEpoch) + } + + c.results.MaxIssuancePerEpoch = c.results.ValidatorRewardsPerEpoch.Mul(c.results.ActiveValidators) + if c.debug { + fmt.Printf("Chain rewards per epoch: %v\n", c.results.MaxIssuancePerEpoch) + } + c.results.MaxIssuancePerYear = c.results.MaxIssuancePerEpoch.Mul(epochsPerYear) + if c.debug { + fmt.Printf("Chain rewards per year: %v\n", c.results.MaxIssuancePerYear) + } + + c.results.Yield = c.results.ValidatorRewardsPerYear.Div(weiPerGwei).Div(weiPerGwei).Div(decimal.New(32, 0)) + if c.debug { + fmt.Printf("Yield: %v\n", c.results.Yield) + } + + return nil +} + +func (c *command) setup(ctx context.Context) error { + var err error + + // Connect to the client. + c.eth2Client, err = util.ConnectToBeaconNode(ctx, c.connection, c.timeout, c.allowInsecureConnections) + if err != nil { + return errors.Wrap(err, "failed to connect to beacon node") + } + + if c.validators == "" { + chainTime, err := standardchaintime.New(ctx, + standardchaintime.WithSpecProvider(c.eth2Client.(eth2client.SpecProvider)), + standardchaintime.WithForkScheduleProvider(c.eth2Client.(eth2client.ForkScheduleProvider)), + standardchaintime.WithGenesisTimeProvider(c.eth2Client.(eth2client.GenesisTimeProvider)), + ) + if err != nil { + return errors.Wrap(err, "failed to set up chaintime service") + } + + // Obtain the number of active validators. + var isProvider bool + validatorsProvider, isProvider := c.eth2Client.(eth2client.ValidatorsProvider) + if !isProvider { + return errors.New("connection does not provide validator information") + } + + validators, err := validatorsProvider.Validators(ctx, "head", nil) + if err != nil { + return errors.Wrap(err, "failed to obtain validators") + } + + currentEpoch := chainTime.CurrentEpoch() + activeValidators := decimal.Zero + activeValidatorBalance := decimal.Zero + for _, validator := range validators { + if validator.Validator.ActivationEpoch <= currentEpoch && + validator.Validator.ExitEpoch > currentEpoch { + activeValidators = activeValidators.Add(one) + activeValidatorBalance = activeValidatorBalance.Add(decimal.NewFromInt(int64(validator.Validator.EffectiveBalance))) + } + } + c.results.ActiveValidators = activeValidators + c.results.ActiveValidatorBalance = activeValidatorBalance.Mul(weiPerGwei) + } else { + activeValidators, err := strconv.ParseInt(c.validators, 0, 64) + if err != nil { + return errors.Wrap(err, "failed to parse number of validators") + } + if activeValidators <= 0 { + return errors.New("number of validators must be greater than 0") + } + + c.results.ActiveValidators = decimal.New(activeValidators, 0) + c.results.ActiveValidatorBalance = decimal.New(32, 0).Mul(c.results.ActiveValidators).Mul(weiPerGwei).Mul(weiPerGwei) + if c.debug { + fmt.Println("Assuming 32Ξ per validator") + } + } + + return nil +} diff --git a/cmd/validator/yield/process_internal_test.go b/cmd/validator/yield/process_internal_test.go new file mode 100644 index 0000000..cde01af --- /dev/null +++ b/cmd/validator/yield/process_internal_test.go @@ -0,0 +1,90 @@ +// 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 TestProcess(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: "InvalidData", + vars: map[string]interface{}{ + "timeout": "60s", + "validators": "1", + "data": "[[", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + }, + }, + { + name: "ValidatorsInvalid", + vars: map[string]interface{}{ + "timeout": "60s", + "validators": "invalid", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + }, + err: "failed to parse number of validators: strconv.ParseInt: parsing \"invalid\": invalid syntax", + }, + { + name: "ValidatorsNegative", + vars: map[string]interface{}{ + "timeout": "60s", + "validators": "-1", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + }, + err: "number of validators must be greater than 0", + }, + { + name: "ValidatorsZero", + vars: map[string]interface{}{ + "timeout": "60s", + "validators": "0", + "connection": os.Getenv("ETHDO_TEST_CONNECTION"), + }, + err: "number of validators must be greater than 0", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + viper.Reset() + + for k, v := range test.vars { + viper.Set(k, v) + } + cmd, err := newCommand(context.Background()) + require.NoError(t, err) + err = cmd.process(context.Background()) + if test.err != "" { + require.EqualError(t, err, test.err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/cmd/validator/yield/run.go b/cmd/validator/yield/run.go new file mode 100644 index 0000000..118847a --- /dev/null +++ b/cmd/validator/yield/run.go @@ -0,0 +1,50 @@ +// 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" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// Run runs the command. +func Run(cmd *cobra.Command) (string, error) { + ctx := context.Background() + + c, err := newCommand(ctx) + if err != nil { + return "", errors.Wrap(err, "failed to set up command") + } + + // Further errors do not need a usage report. + cmd.SilenceUsage = true + + if err := c.process(ctx); err != nil { + return "", errors.Wrap(err, "failed to process") + } + + if viper.GetBool("quiet") { + return "", nil + } + + results, err := c.output(ctx) + if err != nil { + return "", errors.Wrap(err, "failed to obtain output") + } + + return results, nil +} diff --git a/cmd/validatoryield.go b/cmd/validatoryield.go new file mode 100644 index 0000000..e829a0f --- /dev/null +++ b/cmd/validatoryield.go @@ -0,0 +1,61 @@ +// 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 cmd + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + validatoryield "github.com/wealdtech/ethdo/cmd/validator/yield" +) + +var validatorYieldCmd = &cobra.Command{ + Use: "yield", + Short: "Calculate yield for validators", + Long: `Calculate yield for validators. For example: + + ethdo validator yield + +It is important to understand the yield is both probabilistic and dependent on network conditions.`, + RunE: func(cmd *cobra.Command, args []string) error { + res, err := validatoryield.Run(cmd) + if err != nil { + return err + } + if viper.GetBool("quiet") { + return nil + } + res = strings.TrimRight(res, "\n") + fmt.Println(res) + return nil + }, +} + +func init() { + validatorCmd.AddCommand(validatorYieldCmd) + validatorFlags(validatorYieldCmd) + validatorYieldCmd.Flags().String("validators", "", "Number of active validators (default fetches from chain)") + validatorYieldCmd.Flags().Bool("json", false, "JSON output") +} + +func validatorYieldBindings() { + if err := viper.BindPFlag("validators", validatorYieldCmd.Flags().Lookup("validators")); err != nil { + panic(err) + } + if err := viper.BindPFlag("json", validatorYieldCmd.Flags().Lookup("json")); err != nil { + panic(err) + } +} diff --git a/cmd/version.go b/cmd/version.go index 2670cc6..d353688 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -24,7 +24,7 @@ import ( // ReleaseVersion is the release version of the codebase. // Usually overridden by tag names when building binaries. -var ReleaseVersion = "local build (latest release 1.23.1)" +var ReleaseVersion = "local build (latest release 1.24.0)" // versionCmd represents the version command var versionCmd = &cobra.Command{ diff --git a/docs/usage.md b/docs/usage.md index ae1ba2f..4294d0e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -698,6 +698,17 @@ $ ethdo attester inclusion --account=Validators/1 --epoch=6484 Attestation included in block 207492 (inclusion delay 1) ``` +#### `yield` + +`ethdo validator yield` calculates the expected yield given the number of validators. Options include: + - `validators` use a specified number of validators rather than the current number of active validators + - `json` obtain detailed information in JSON format + +```sh +$ ethdo validator yield +Yield: 4.64% +``` + ## Maintainers Jim McDonald: [@mcdee](https://github.com/mcdee). diff --git a/go.mod b/go.mod index 2ededb2..7356586 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 github.com/rs/zerolog v1.26.1 + github.com/shopspring/decimal v1.3.1 github.com/spf13/afero v1.8.0 // indirect github.com/spf13/cobra v1.3.0 github.com/spf13/pflag v1.0.5 @@ -42,7 +43,7 @@ require ( github.com/wealdtech/go-eth2-wallet-store-s3 v1.10.0 github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.0 github.com/wealdtech/go-eth2-wallet-types/v2 v2.9.0 - github.com/wealdtech/go-string2eth v1.1.0 + github.com/wealdtech/go-string2eth v1.2.0 golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect golang.org/x/text v0.3.7 google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect diff --git a/go.sum b/go.sum index 2a44729..f075e4c 100644 --- a/go.sum +++ b/go.sum @@ -75,8 +75,6 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI github.com/attestantio/dirk v1.1.0 h1:hwMTYZkwj/Y0um3OD0LQxg2xSl4/5xqVWV2MRePE4ec= github.com/attestantio/dirk v1.1.0/go.mod h1:2jkOw/XHjvIDdhDcmj+Z3kuVPpxMcQ6zxzzjSSv71PY= github.com/attestantio/go-eth2-client v0.8.1/go.mod h1:kEK9iAAOBoADO5wEkd84FEOzjT1zXgVWveQsqn+uBGg= -github.com/attestantio/go-eth2-client v0.10.0 h1:nmOmzErfz4I2gEkucHKOaFwkbwD4i6JbIX38Z8Dm4Tc= -github.com/attestantio/go-eth2-client v0.10.0/go.mod h1:ijuXoXJCBFMexUYaBOl8PXfZKwYUFJy7cV03TMdw8Bo= github.com/attestantio/go-eth2-client v0.11.0 h1:8/Jn5AAfd+4tOggLi+FvOv9/ORaObECv42ab7vK2FJc= github.com/attestantio/go-eth2-client v0.11.0/go.mod h1:zXL/BxC0cBBhxj+tP7QG7t9Ufoa8GwQLdlbvZRd9+dM= github.com/aws/aws-sdk-go v1.33.17/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= @@ -354,7 +352,6 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.11 h1:i2lw1Pm7Yi/4O6XCSyJWqEHI2MDw2FzUK6o/D21xn2A= github.com/klauspost/cpuid/v2 v2.0.11/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= @@ -495,6 +492,8 @@ github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDN github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1fOy4Ee11vHhUFHQNpHhrBneOCNHVXS5w= github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= @@ -589,6 +588,8 @@ github.com/wealdtech/go-indexer v1.0.0/go.mod h1:u1cjsbsOXsm5jzJDyLmZY7GsrdX8KYX github.com/wealdtech/go-majordomo v1.0.1/go.mod h1:QoT4S1nUQwdQK19+CfepDwV+Yr7cc3dbF+6JFdQnIqY= github.com/wealdtech/go-string2eth v1.1.0 h1:USJQmysUrBYYmZs7d45pMb90hRSyEwizP7lZaOZLDAw= github.com/wealdtech/go-string2eth v1.1.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU= +github.com/wealdtech/go-string2eth v1.2.0 h1:C0E5p78tecZTsGccJc9r/kreFah4EfDs5uUPnS6XXMs= +github.com/wealdtech/go-string2eth v1.2.0/go.mod h1:RUzsLjJtbZaJ/3UKn9kY19a/vCCUHtEWoUW3uiK6yGU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -843,8 +844,6 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=