diff --git a/CHANGELOG.md b/CHANGELOG.md index dd97bdf733..1d6ddb935b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1319](https://github.com/umee-network/umee/pull/1319) Implemented MaxSupply. - [1331](https://github.com/umee-network/umee/pull/1331) Implemented MinCollateralLiquidity. - [1343](https://github.com/umee-network/umee/pull/1343) RepayBadDebt and Liquidate automatically clear blacklisted collateral. +- [1379](https://github.com/umee-network/umee/pull/1379) Add `mininumCommissionRate` update to all validators. ### Improvements diff --git a/app/upgrades.go b/app/upgrades.go index 96c96b9a47..2f8e466840 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -11,6 +11,7 @@ import ( bech32ibckeeper "github.com/osmosis-labs/bech32-ibc/x/bech32ibc/keeper" bech32ibctypes "github.com/osmosis-labs/bech32-ibc/x/bech32ibc/types" + v3upgrades "github.com/umee-network/umee/v3/app/upgrades/v3" leveragetypes "github.com/umee-network/umee/v3/x/leverage/types" oracletypes "github.com/umee-network/umee/v3/x/oracle/types" ) @@ -30,7 +31,30 @@ func (app UmeeApp) RegisterUpgradeHandlers() { } ctx.Logger().Info("Upgrade handler execution finished, running migrations", "name", UpgradeV3_0Plan) - return app.mm.RunMigrations(ctx, app.configurator, fromVM) + vm, err := app.mm.RunMigrations(ctx, app.configurator, fromVM) + if err != nil { + return vm, err + } + + ctx.Logger().Info("Upgrade handler execution finished, updating minimum commission rate param of staking module", + "name", UpgradeV3_0Plan) + minCommissionRate, err := v3upgrades.UpdateMinimumCommissionRateParam(ctx, app.StakingKeeper) + if err != nil { + return vm, sdkerrors.Wrapf( + err, "Calypso %q Upgrade: Unable to upgrade, failed to update minimum commission rate param of staking module", + UpgradeV3_0Plan) + } + + ctx.Logger().Info("Upgrade handler execution finished, updating minimum commission rate of all validators", + "name", UpgradeV3_0Plan) + err = v3upgrades.SetMinimumCommissionRateToValidatros(ctx, app.StakingKeeper, minCommissionRate) + if err != nil { + return vm, sdkerrors.Wrapf( + err, "Calypso %q Upgrade: Unable to upgrade, failed to update minimum commission rate for validators", + UpgradeV3_0Plan) + } + + return vm, err }) upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() diff --git a/app/upgrades/v3/expected_types.go b/app/upgrades/v3/expected_types.go new file mode 100644 index 0000000000..9526cc6038 --- /dev/null +++ b/app/upgrades/v3/expected_types.go @@ -0,0 +1,15 @@ +package v3 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +type StakingKeeper interface { + GetParams(ctx sdk.Context) types.Params + SetParams(ctx sdk.Context, params types.Params) + GetAllValidators(ctx sdk.Context) (validators []types.Validator) + BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error + SetValidator(ctx sdk.Context, validator types.Validator) + GetValidator(ctx sdk.Context, addr sdk.ValAddress) (types.Validator, bool) +} diff --git a/app/upgrades/v3/min_commission.go b/app/upgrades/v3/min_commission.go new file mode 100644 index 0000000000..7c4f479b8e --- /dev/null +++ b/app/upgrades/v3/min_commission.go @@ -0,0 +1,42 @@ +package v3 + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + minCommissionRate = sdk.MustNewDecFromStr("0.05") +) + +// UpdateMinimumCommissionRateParam is update the minimum commission rate param of staking. +func UpdateMinimumCommissionRateParam(ctx sdk.Context, keeper StakingKeeper) (sdk.Dec, error) { + params := keeper.GetParams(ctx) + // update the minCommissionRate param + params.MinCommissionRate = minCommissionRate + + keeper.SetParams(ctx, params) + + return minCommissionRate, nil +} + +// SetMinimumCommissionRateToValidatros is update the minimum commission rate to the validators rate +// whose commission rate is below the minimum commission rate. +func SetMinimumCommissionRateToValidatros( + ctx sdk.Context, keeper StakingKeeper, minCommissionRate sdk.Dec) error { + validators := keeper.GetAllValidators(ctx) + + for _, validator := range validators { + if validator.Commission.Rate.IsNil() || validator.Commission.Rate.LT(minCommissionRate) { + if err := keeper.BeforeValidatorModified(ctx, validator.GetOperator()); err != nil { + return err + } + + validator.Commission.Rate = minCommissionRate + validator.Commission.UpdateTime = ctx.BlockTime() + + keeper.SetValidator(ctx, validator) + } + } + + return nil +} diff --git a/app/upgrades/v3/min_commission_test.go b/app/upgrades/v3/min_commission_test.go new file mode 100644 index 0000000000..6aeefcbe15 --- /dev/null +++ b/app/upgrades/v3/min_commission_test.go @@ -0,0 +1,51 @@ +package v3 + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +func TestUpdateMinimumCommissionRateParam(t *testing.T) { + ctx := sdk.NewContext(nil, tmproto.Header{}, false, nil) + _, sk := GenerateRandomTestCase() + + // check default min commission rate + oldParams := sk.GetParams(ctx) + require.Equal(t, types.DefaultMinCommissionRate, oldParams.MinCommissionRate) + + // update the min commission rate + _, err := UpdateMinimumCommissionRateParam(ctx, &sk) + require.NoError(t, err) + + // get the updated params + updatedParams := sk.GetParams(ctx) + require.Equal(t, minCommissionRate, updatedParams.MinCommissionRate) +} + +func TestSetMinimumCommissionRateToValidatros(t *testing.T) { + ctx := sdk.NewContext(nil, tmproto.Header{}, false, nil) + valAddrs, sk := GenerateRandomTestCase() + + // update the min commission rate + minCommissionRate, err := UpdateMinimumCommissionRateParam(ctx, &sk) + require.NoError(t, err) + require.NotNil(t, minCommissionRate) + + // update min commisson rate to all validators + err = SetMinimumCommissionRateToValidatros(ctx, &sk, minCommissionRate) + require.NoError(t, err) + + // get the validator + validator, found := sk.GetValidator(ctx, valAddrs[0]) + require.True(t, found) + require.True(t, minCommissionRate.Equal(validator.Commission.Rate)) + + validator2, found := sk.GetValidator(ctx, valAddrs[1]) + require.True(t, found) + // validator2 commission rate should be greater than minCommissionRate + require.True(t, minCommissionRate.LT(validator2.Commission.Rate)) +} diff --git a/app/upgrades/v3/staking_util_test.go b/app/upgrades/v3/staking_util_test.go new file mode 100644 index 0000000000..039f69bf62 --- /dev/null +++ b/app/upgrades/v3/staking_util_test.go @@ -0,0 +1,106 @@ +package v3 + +import ( + "crypto/rand" + "math/big" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +var ( + _ StakingKeeper = &MockStakingKeeper{} +) + +// MockStakingKeeper imlements the StakingKeeper interface. +type MockStakingKeeper struct { + validators []types.Validator + params types.Params +} + +// GetValidator implements StakingKeeper +func (m *MockStakingKeeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (types.Validator, bool) { + var ( + validator types.Validator + found bool + ) + + for _, v := range m.validators { + if v.GetOperator().Equals(addr) { + found = true + validator = v + break + } + } + + return validator, found +} + +// BeforeValidatorModified implements StakingKeeper +func (*MockStakingKeeper) BeforeValidatorModified(ctx sdk.Context, valAddr sdk.ValAddress) error { + return nil +} + +// GetAllValidators implements StakingKeeper +func (m *MockStakingKeeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { + return m.validators +} + +// GetParams implements StakingKeeper +func (m *MockStakingKeeper) GetParams(ctx sdk.Context) types.Params { + return m.params +} + +// SetParams implements StakingKeeper +func (m *MockStakingKeeper) SetParams(ctx sdk.Context, params types.Params) { + m.params = params +} + +// SetValidator implements StakingKeeper +func (m *MockStakingKeeper) SetValidator(ctx sdk.Context, validator types.Validator) { + for index, v := range m.validators { + if v.GetOperator().Equals(validator.GetOperator()) { + m.validators[index] = validator + break + } + } +} + +// GenerateRandomTestCase +func GenerateRandomTestCase() ([]sdk.ValAddress, MockStakingKeeper) { + mockValidators := []types.Validator{} + + var valAddrs []sdk.ValAddress + randNum, _ := rand.Int(rand.Reader, big.NewInt(10000)) + numInputs := 10 + int((randNum.Int64() % 100)) + for i := 0; i < numInputs; i++ { + pubKey := secp256k1.GenPrivKey().PubKey() + valValAddr := sdk.ValAddress(pubKey.Address()) + mockValidator, _ := types.NewValidator(valValAddr, pubKey, types.Description{}) + mockValidators = append(mockValidators, mockValidator) + } + + // adding 0.01 to first validator + val := mockValidators[0] + val.Commission.Rate = sdk.MustNewDecFromStr("0.01") + mockValidators[0] = val + + // adding more then minimumCommissionRate to validator 2 + val = mockValidators[1] + val.Commission.Rate = types.DefaultMinCommissionRate.Add(sdk.MustNewDecFromStr("1")) + mockValidators[1] = val + + valAddrs = []sdk.ValAddress{mockValidators[0].GetOperator(), mockValidators[1].GetOperator()} + + stakingKeeper := NewMockStakingKeeper(mockValidators) + + return valAddrs, stakingKeeper +} + +func NewMockStakingKeeper(validatros []types.Validator) MockStakingKeeper { + return MockStakingKeeper{ + validators: validatros, + params: types.DefaultParams(), + } +}