diff --git a/x/staking/keeper/liquid_stake.go b/x/staking/keeper/liquid_stake.go index a0e9df1c974b..2aece330d283 100644 --- a/x/staking/keeper/liquid_stake.go +++ b/x/staking/keeper/liquid_stake.go @@ -138,43 +138,67 @@ func (k Keeper) DecreaseTotalLiquidStakedTokens(ctx sdk.Context, amount sdk.Int) // and the total liquid staked shares cannot exceed the validator bond cap // 1) (TotalLiquidStakedTokens / TotalStakedTokens) <= ValidatorLiquidStakingCap // 2) LiquidShares <= (ValidatorBondShares * ValidatorBondFactor) -func (k Keeper) SafelyIncreaseValidatorLiquidShares(ctx sdk.Context, validator types.Validator, shares sdk.Dec) error { +func (k Keeper) SafelyIncreaseValidatorLiquidShares(ctx sdk.Context, valAddress sdk.ValAddress, shares sdk.Dec) (types.Validator, error) { + validator, found := k.GetValidator(ctx, valAddress) + if !found { + return validator, types.ErrNoValidatorFound + } + // Confirm the validator bond factor and validator liquid staking cap will not be exceeded if k.CheckExceedsValidatorBondCap(ctx, validator, shares) { - return types.ErrInsufficientValidatorBondShares + return validator, types.ErrInsufficientValidatorBondShares } if k.CheckExceedsValidatorLiquidStakingCap(ctx, validator, shares) { - return types.ErrValidatorLiquidStakingCapExceeded + return validator, types.ErrValidatorLiquidStakingCapExceeded } // Increment the validator's liquid shares validator.LiquidShares = validator.LiquidShares.Add(shares) k.SetValidator(ctx, validator) - return nil + return validator, nil } // DecreaseValidatorLiquidShares decrements the liquid shares on a validator -func (k Keeper) DecreaseValidatorLiquidShares(ctx sdk.Context, validator types.Validator, shares sdk.Dec) error { +func (k Keeper) DecreaseValidatorLiquidShares(ctx sdk.Context, valAddress sdk.ValAddress, shares sdk.Dec) (types.Validator, error) { + validator, found := k.GetValidator(ctx, valAddress) + if !found { + return validator, types.ErrNoValidatorFound + } + if shares.GT(validator.LiquidShares) { - return types.ErrValidatorLiquidSharesUnderflow + return validator, types.ErrValidatorLiquidSharesUnderflow } + validator.LiquidShares = validator.LiquidShares.Sub(shares) k.SetValidator(ctx, validator) - return nil + + return validator, nil } // Increase validator bond shares increments the validator's self bond // in the event that the delegation amount on a validator bond delegation is increased -func (k Keeper) IncreaseValidatorBondShares(ctx sdk.Context, validator types.Validator, shares sdk.Dec) { +func (k Keeper) IncreaseValidatorBondShares(ctx sdk.Context, valAddress sdk.ValAddress, shares sdk.Dec) error { + validator, found := k.GetValidator(ctx, valAddress) + if !found { + return types.ErrNoValidatorFound + } + validator.ValidatorBondShares = validator.ValidatorBondShares.Add(shares) k.SetValidator(ctx, validator) + + return nil } // SafelyDecreaseValidatorBond decrements the validator's self bond // so long as it will not cause the current delegations to exceed the threshold // set by validator bond factor -func (k Keeper) SafelyDecreaseValidatorBond(ctx sdk.Context, validator types.Validator, shares sdk.Dec) error { +func (k Keeper) SafelyDecreaseValidatorBond(ctx sdk.Context, valAddress sdk.ValAddress, shares sdk.Dec) error { + validator, found := k.GetValidator(ctx, valAddress) + if !found { + return types.ErrNoValidatorFound + } + // Check if the decreased self bond will cause the validator bond threshold to be exceeded validatorBondFactor := k.ValidatorBondFactor(ctx) validatorBondEnabled := !validatorBondFactor.Equal(types.ValidatorBondCapDisabled) diff --git a/x/staking/keeper/liquid_stake_test.go b/x/staking/keeper/liquid_stake_test.go index 36d0e8c6d10a..639b8a311d31 100644 --- a/x/staking/keeper/liquid_stake_test.go +++ b/x/staking/keeper/liquid_stake_test.go @@ -639,7 +639,7 @@ func TestSafelyIncreaseValidatorLiquidShares(t *testing.T) { validatorTotalShares := sdk.NewDec(75) firstIncreaseAmount := sdk.NewDec(20) - secondIncreaseAmount := sdk.NewDec(40) + secondIncreaseAmount := sdk.NewDec(10) // total increase of 30 initialBondFactor := sdk.NewDec(1) finalBondFactor := sdk.NewDec(10) @@ -665,7 +665,7 @@ func TestSafelyIncreaseValidatorLiquidShares(t *testing.T) { // Attempt to increase the validator liquid shares, it should throw an // error that the validator bond cap was exceeded - err := app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, initialValidator, firstIncreaseAmount) + _, err := app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, valAddress, firstIncreaseAmount) require.ErrorIs(t, err, types.ErrInsufficientValidatorBondShares) checkValidatorLiquidShares(initialLiquidShares, "shares after low bond factor") @@ -675,12 +675,12 @@ func TestSafelyIncreaseValidatorLiquidShares(t *testing.T) { // Try the increase again and check that it succeeded expectedLiquidSharesAfterFirstStake := initialLiquidShares.Add(firstIncreaseAmount) - err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, initialValidator, firstIncreaseAmount) + _, err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, valAddress, firstIncreaseAmount) require.NoError(t, err) checkValidatorLiquidShares(expectedLiquidSharesAfterFirstStake, "shares with cap loose bond cap") // Attempt another increase, it should fail from the liquid staking cap - err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, initialValidator, secondIncreaseAmount) + _, err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, valAddress, secondIncreaseAmount) require.ErrorIs(t, err, types.ErrValidatorLiquidStakingCapExceeded) checkValidatorLiquidShares(expectedLiquidSharesAfterFirstStake, "shares after liquid staking cap hit") @@ -689,8 +689,8 @@ func TestSafelyIncreaseValidatorLiquidShares(t *testing.T) { app.StakingKeeper.SetParams(ctx, params) // Finally confirm that the increase succeeded this time - expectedLiquidSharesAfterSecondStake := initialLiquidShares.Add(secondIncreaseAmount) - err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, initialValidator, secondIncreaseAmount) + expectedLiquidSharesAfterSecondStake := expectedLiquidSharesAfterFirstStake.Add(secondIncreaseAmount) + _, err = app.StakingKeeper.SafelyIncreaseValidatorLiquidShares(ctx, valAddress, secondIncreaseAmount) require.NoError(t, err, "no error expected after increasing liquid staking cap") checkValidatorLiquidShares(expectedLiquidSharesAfterSecondStake, "shares after loose liquid stake cap") } @@ -714,7 +714,7 @@ func TestDecreaseValidatorLiquidShares(t *testing.T) { app.StakingKeeper.SetValidator(ctx, initialValidator) // Decrease the validator liquid shares, and confirm the new share amount has been updated - err := app.StakingKeeper.DecreaseValidatorLiquidShares(ctx, initialValidator, decreaseAmount) + _, err := app.StakingKeeper.DecreaseValidatorLiquidShares(ctx, valAddress, decreaseAmount) require.NoError(t, err, "no error expected when decreasing validator liquid shares") actualValidator, found := app.StakingKeeper.GetValidator(ctx, valAddress) @@ -722,7 +722,7 @@ func TestDecreaseValidatorLiquidShares(t *testing.T) { require.Equal(t, initialLiquidShares.Sub(decreaseAmount), actualValidator.LiquidShares, "liquid shares") // Attempt to decrease by a larger amount than it has, it should fail - err = app.StakingKeeper.DecreaseValidatorLiquidShares(ctx, actualValidator, initialLiquidShares) + _, err = app.StakingKeeper.DecreaseValidatorLiquidShares(ctx, valAddress, initialLiquidShares) require.ErrorIs(t, err, types.ErrValidatorLiquidSharesUnderflow) } @@ -758,7 +758,7 @@ func TestSafelyDecreaseValidatorBond(t *testing.T) { // from (100 * 10 = 1000) to (100 * 5 = 500) // Since this is still above the initial liquid shares of 200, this will succeed decreaseAmount, expectedBondShares := sdk.NewDec(5), sdk.NewDec(5) - err := app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, initialValidator, decreaseAmount) + err := app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, valAddress, decreaseAmount) require.NoError(t, err) actualValidator, found := app.StakingKeeper.GetValidator(ctx, valAddress) @@ -769,7 +769,7 @@ func TestSafelyDecreaseValidatorBond(t *testing.T) { // This time, the cap will be reduced to (factor * shares) = (100 * 1) = 100 // However, the liquid shares are currently 200, so this should fail decreaseAmount, expectedBondShares = sdk.NewDec(4), sdk.NewDec(1) - err = app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, actualValidator, decreaseAmount) + err = app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, valAddress, decreaseAmount) require.ErrorIs(t, err, types.ErrInsufficientValidatorBondShares) // Finally, disable the cap and attempt to decrease again @@ -777,7 +777,7 @@ func TestSafelyDecreaseValidatorBond(t *testing.T) { params.ValidatorBondFactor = types.ValidatorBondCapDisabled app.StakingKeeper.SetParams(ctx, params) - err = app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, actualValidator, decreaseAmount) + err = app.StakingKeeper.SafelyDecreaseValidatorBond(ctx, valAddress, decreaseAmount) require.NoError(t, err) actualValidator, found = app.StakingKeeper.GetValidator(ctx, valAddress) diff --git a/x/staking/keeper/msg_server.go b/x/staking/keeper/msg_server.go index 01272557be20..d42b75c9f67e 100644 --- a/x/staking/keeper/msg_server.go +++ b/x/staking/keeper/msg_server.go @@ -215,15 +215,10 @@ func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*typ if err := k.SafelyIncreaseTotalLiquidStakedTokens(ctx, tokens, false); err != nil { return nil, err } - if err := k.SafelyIncreaseValidatorLiquidShares(ctx, validator, shares); err != nil { + validator, err = k.SafelyIncreaseValidatorLiquidShares(ctx, valAddr, shares) + if err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the validator's liquid shares were updated above - validator, found = k.GetValidator(ctx, valAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } // NOTE: source funds are always unbonded @@ -238,7 +233,9 @@ func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*typ return nil, types.ErrNoDelegation } if delegation.ValidatorBond { - k.IncreaseValidatorBondShares(ctx, validator, newShares) + if err := k.IncreaseValidatorBondShares(ctx, valAddr, newShares); err != nil { + return nil, err + } } if tokens.IsInt64() { @@ -282,7 +279,7 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed return nil, err } - srcValidator, found := k.GetValidator(ctx, valSrcAddr) + _, found := k.GetValidator(ctx, valSrcAddr) if !found { return nil, types.ErrNoValidatorFound } @@ -313,15 +310,9 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed // If this is a validator self-bond, the new liquid delegation cannot fall below the self-bond * bond factor // The delegation on the new validator will not a validator bond if srcDelegation.ValidatorBond { - if err := k.SafelyDecreaseValidatorBond(ctx, srcValidator, srcShares); err != nil { + if err := k.SafelyDecreaseValidatorBond(ctx, valSrcAddr, srcShares); err != nil { return nil, err } - // Note: this is required for downstream uses of the srcValidator variable - // since the validator bond shares were updated above - srcValidator, found = k.GetValidator(ctx, valSrcAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } // If this delegation from a liquid staker, the delegation on the new validator @@ -332,18 +323,12 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed if err != nil { return nil, err } - if err := k.SafelyIncreaseValidatorLiquidShares(ctx, dstValidator, dstShares); err != nil { + if _, err := k.SafelyIncreaseValidatorLiquidShares(ctx, valDstAddr, dstShares); err != nil { return nil, err } - if err := k.DecreaseValidatorLiquidShares(ctx, srcValidator, srcShares); err != nil { + if _, err := k.DecreaseValidatorLiquidShares(ctx, valSrcAddr, srcShares); err != nil { return nil, err } - // Note: this is required for downstream uses of each validator variable - // since the liquid shares were updated above - srcValidator, found = k.GetValidator(ctx, valSrcAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } bondDenom := k.BondDenom(ctx) @@ -370,7 +355,9 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed if err != nil { return nil, err } - k.IncreaseValidatorBondShares(ctx, dstValidator, dstShares) + if err := k.IncreaseValidatorBondShares(ctx, valDstAddr, dstShares); err != nil { + return nil, err + } } if msg.Amount.Amount.IsInt64() { @@ -425,7 +412,7 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) ( return nil, err } - validator, found := k.GetValidator(ctx, addr) + _, found := k.GetValidator(ctx, addr) if !found { return nil, types.ErrNoValidatorFound } @@ -441,15 +428,9 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) ( // if this is a validator self-bond, the new liquid delegation cannot fall below the self-bond * bond factor if delegation.ValidatorBond { - if err := k.SafelyDecreaseValidatorBond(ctx, validator, shares); err != nil { + if err := k.SafelyDecreaseValidatorBond(ctx, addr, shares); err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the validator bond shares was updated above - validator, found = k.GetValidator(ctx, addr) - if !found { - return nil, types.ErrNoValidatorFound - } } // if this delegation is from a liquid staking provider (identified if the delegator @@ -458,15 +439,9 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) ( if err := k.DecreaseTotalLiquidStakedTokens(ctx, tokens); err != nil { return nil, err } - if err := k.DecreaseValidatorLiquidShares(ctx, validator, shares); err != nil { + if _, err := k.DecreaseValidatorLiquidShares(ctx, addr, shares); err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the liquid shares were updated above - validator, found = k.GetValidator(ctx, addr) - if !found { - return nil, types.ErrNoValidatorFound - } } bondDenom := k.BondDenom(ctx) @@ -574,15 +549,10 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.M if err := k.SafelyIncreaseTotalLiquidStakedTokens(ctx, tokens, false); err != nil { return nil, err } - if err := k.SafelyIncreaseValidatorLiquidShares(ctx, validator, shares); err != nil { + validator, err = k.SafelyIncreaseValidatorLiquidShares(ctx, valAddr, shares) + if err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the validator's liquid shares were updated above - validator, found = k.GetValidator(ctx, valAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } var ( @@ -621,7 +591,9 @@ func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.M return nil, types.ErrNoDelegation } if delegation.ValidatorBond { - k.IncreaseValidatorBondShares(ctx, validator, newShares) + if err := k.IncreaseValidatorBondShares(ctx, valAddr, newShares); err != nil { + return nil, err + } } amount := unbondEntry.Balance.Sub(msg.Amount.Amount) @@ -746,15 +718,10 @@ func (k msgServer) TokenizeShares(goCtx context.Context, msg *types.MsgTokenizeS if err := k.SafelyIncreaseTotalLiquidStakedTokens(ctx, msg.Amount.Amount, true); err != nil { return nil, err } - if err := k.SafelyIncreaseValidatorLiquidShares(ctx, validator, shares); err != nil { + validator, err = k.SafelyIncreaseValidatorLiquidShares(ctx, valAddr, shares) + if err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the validator's liquid shares were updated above - validator, found = k.GetValidator(ctx, valAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } recordID := k.GetLastTokenizeShareRecordID(ctx) + 1 @@ -894,15 +861,10 @@ func (k msgServer) RedeemTokensForShares(goCtx context.Context, msg *types.MsgRe if err := k.DecreaseTotalLiquidStakedTokens(ctx, tokens); err != nil { return nil, err } - if err := k.DecreaseValidatorLiquidShares(ctx, validator, shares); err != nil { + validator, err = k.DecreaseValidatorLiquidShares(ctx, valAddr, shares) + if err != nil { return nil, err } - // Note: this is required for downstream uses of the validator variable - // since the liquid shares were updated above - validator, found = k.GetValidator(ctx, valAddr) - if !found { - return nil, types.ErrNoValidatorFound - } } returnAmount, err := k.Unbond(ctx, record.GetModuleAddress(), valAddr, shares)